001/* 002 * jPOS Project [http://jpos.org] 003 * Copyright (C) 2000-2026 jPOS Software SRL 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Affero General Public License as 007 * published by the Free Software Foundation, either version 3 of the 008 * License, or (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Affero General Public License for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018 019package org.jpos.iso.channel; 020 021import org.jpos.core.Configuration; 022import org.jpos.core.ConfigurationException; 023import org.jpos.iso.*; 024import org.jpos.iso.header.BASE1Header; 025import org.jpos.util.LogEvent; 026import org.jpos.util.Logger; 027 028import java.io.IOException; 029import java.net.ServerSocket; 030 031/** 032 * ISOChannel implementation - VISA's VAP framing 033 * 034 * @author apr@cs.com.uy 035 * @version $Id$ 036 * @see ISOMsg 037 * @see ISOException 038 * @see ISOChannel 039 */ 040@SuppressWarnings("unused") 041public class VAPChannel extends BaseChannel { 042 String srcid = "000000"; 043 String dstid = "000000"; 044 boolean debugPoll; 045 int headerFormat = 2; 046 private boolean replyKeepAlive = true; 047 private boolean swapDirection = false; 048 049 /** 050 * Public constructor (used by Class.forName("...").newInstance()) 051 */ 052 public VAPChannel () { 053 super(); 054 } 055 /** 056 * Construct client ISOChannel 057 * @param host server TCP Address 058 * @param port server port number 059 * @param p an ISOPackager (should be ISO87BPackager) 060 * @see org.jpos.iso.packager.ISO87BPackager 061 */ 062 public VAPChannel (String host, int port, ISOPackager p) { 063 super(host, port, p); 064 } 065 /** 066 * Construct server ISOChannel 067 * @param p an ISOPackager (should be ISO87BPackager) 068 * @exception IOException on I/O error 069 * @see org.jpos.iso.packager.ISO87BPackager 070 */ 071 public VAPChannel (ISOPackager p) throws IOException { 072 super(p); 073 } 074 /** 075 * constructs a server ISOChannel associated with a Server Socket 076 * @param p an ISOPackager 077 * @param serverSocket where to accept a connection 078 * @exception IOException on I/O error 079 * @see ISOPackager 080 */ 081 public VAPChannel (ISOPackager p, ServerSocket serverSocket) 082 throws IOException 083 { 084 super(p, serverSocket); 085 } 086 /** 087 * Sets the source identifier carried in the BASE1 header. 088 * 089 * @param srcid source id 090 */ 091 public void setSrcId (String srcid) { 092 this.srcid = srcid; 093 } 094 /** 095 * Returns the configured source identifier. 096 * 097 * @return source id 098 */ 099 public String getSrcId () { 100 return srcid; 101 } 102 /** 103 * Sets the destination identifier carried in the BASE1 header. 104 * 105 * @param dstid destination id 106 */ 107 public void setDstId (String dstid) { 108 this.dstid = dstid; 109 } 110 /** 111 * Returns the configured destination identifier. 112 * 113 * @return destination id 114 */ 115 public String getDstId () { 116 return dstid; 117 } 118 /** 119 * The default header for VAPChannel is BASE1Header 120 */ 121 protected ISOHeader getDynamicHeader (byte[] image) { 122 return new BASE1Header(image); 123 } 124 125 /** 126 * This method reads in a Base 1 Header. 127 * 128 * @param hLen requested header length (ignored — the actual length is read from the wire) 129 * @return the header bytes read from the wire 130 * @throws IOException on I/O error 131 */ 132 protected byte[] readHeader(int hLen) 133 throws IOException { 134 // Read the first byte of the header (header length) 135 int b = serverIn.read(); 136 int bytesRead = b; 137 138 if (b != -1) 139 { 140 // Create am array to read the header into 141 byte bytes[] = new byte[b]; 142 // Stick the first byte in 143 bytes[0] = (byte) b; 144 // and read the rest of the header 145 serverIn.readFully(bytes, 1, b - 1); 146 147 // Is there another header following on 148 if ((bytes[1] & 0x80) == 0x80) 149 { 150 b = serverIn.read(); 151 bytesRead += b; 152 153 // Create an array big enough for both headers 154 byte tmp[] = new byte[bytesRead]; 155 156 // Copy in the original 157 System.arraycopy(bytes, 0, tmp, 0, bytes.length); 158 159 // And this one 160 tmp[bytes.length] = (byte) b; 161 serverIn.readFully(tmp, bytes.length + 1, b - 1); 162 bytes = tmp; 163 } 164 return bytes; 165 } 166 else 167 { 168 throw new IOException("Error reading header"); 169 } 170 } 171 172 protected void sendMessageLength(int len) throws IOException { 173 serverOut.write (len >> 8); 174 serverOut.write (len); 175 serverOut.write (0); 176 serverOut.write (0); 177 } 178 /** 179 * @param m the message 180 * @param len already packed message len (to avoid re-pack) 181 * @exception IOException on I/O error 182 */ 183 protected void sendMessageHeader(ISOMsg m, int len) 184 throws IOException 185 { 186 ISOHeader h = !isOverrideHeader() && m.getHeader() != null ? 187 m.getISOHeader() : 188 new BASE1Header (srcid, dstid, headerFormat); 189 190 if (h instanceof BASE1Header) 191 ((BASE1Header)h).setLen(len); 192 193 serverOut.write(h.pack()); 194 } 195 protected int getMessageLength() throws IOException, ISOException { 196 int l = 0; 197 byte[] b = new byte[4]; 198 // ignore VAP polls (0 message length) 199 while (l == 0) { 200 serverIn.readFully(b,0,4); 201 l = ((int)b[0] &0xFF) << 8 | (int)b[1] &0xFF; 202 203 if (replyKeepAlive && l == 0) { 204 try { 205 serverOutLock.lock(); 206 serverOut.write(b); 207 serverOut.flush(); 208 if (debugPoll) 209 Logger.log(new LogEvent(this, "poll")); 210 } finally { 211 serverOutLock.unlock(); 212 } 213 } 214 } 215 return l; 216 } 217 218 protected int getHeaderLength() { 219 return BASE1Header.LENGTH; 220 } 221 222 protected boolean isRejected(byte[] b) { 223 BASE1Header h = new BASE1Header(b); 224 return h.isRejected() || h.getHLen() != BASE1Header.LENGTH; 225 } 226 227 protected boolean shouldIgnore (byte[] b) { 228 if (b != null) { 229 BASE1Header h = new BASE1Header(b); 230 return h.getFormat() > 2; 231 } 232 return false; 233 } 234 235 236 /** 237 * sends an ISOMsg over the TCP/IP session. 238 * 239 * swap source/destination addresses in BASE1Header if 240 * a reply message is detected.<br> 241 * Sending an incoming message is seen as a reply. 242 * 243 * @param m the Message to be sent 244 * @exception IOException on I/O error 245 * @exception ISOException on pack/unpack error 246 * @see ISOChannel#send 247 */ 248 public void send (ISOMsg m) throws IOException, ISOException 249 { 250 if (m.isIncoming() && m.getHeader() != null && swapDirection) { 251 m.getISOHeader().swapDirection(); 252 } 253 super.send(m); 254 } 255 256 public void setConfiguration (Configuration cfg) 257 throws ConfigurationException 258 { 259 super.setConfiguration (cfg); 260 srcid = cfg.get ("srcid", "000000"); 261 dstid = cfg.get ("dstid", "000000"); 262 debugPoll = cfg.getBoolean("debug-poll", false); 263 headerFormat = cfg.getInt("header-format", 2); 264 replyKeepAlive = cfg.getBoolean("reply-keepalive", true); 265 swapDirection = cfg.getBoolean("swap-direction", true); 266 } 267}