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 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 079 * @see ISOPackager 080 */ 081 public VAPChannel (ISOPackager p, ServerSocket serverSocket) 082 throws IOException 083 { 084 super(p, serverSocket); 085 } 086 public void setSrcId (String srcid) { 087 this.srcid = srcid; 088 } 089 public String getSrcId () { 090 return srcid; 091 } 092 public void setDstId (String dstid) { 093 this.dstid = dstid; 094 } 095 public String getDstId () { 096 return dstid; 097 } 098 /** 099 * The default header for VAPChannel is BASE1Header 100 */ 101 protected ISOHeader getDynamicHeader (byte[] image) { 102 return new BASE1Header(image); 103 } 104 105 /** 106 * This method reads in a Base 1 Header. 107 * 108 * @param hLen 109 * @throws IOException 110 */ 111 protected byte[] readHeader(int hLen) 112 throws IOException { 113 // Read the first byte of the header (header length) 114 int b = serverIn.read(); 115 int bytesRead = b; 116 117 if (b != -1) 118 { 119 // Create am array to read the header into 120 byte bytes[] = new byte[b]; 121 // Stick the first byte in 122 bytes[0] = (byte) b; 123 // and read the rest of the header 124 serverIn.readFully(bytes, 1, b - 1); 125 126 // Is there another header following on 127 if ((bytes[1] & 0x80) == 0x80) 128 { 129 b = serverIn.read(); 130 bytesRead += b; 131 132 // Create an array big enough for both headers 133 byte tmp[] = new byte[bytesRead]; 134 135 // Copy in the original 136 System.arraycopy(bytes, 0, tmp, 0, bytes.length); 137 138 // And this one 139 tmp[bytes.length] = (byte) b; 140 serverIn.readFully(tmp, bytes.length + 1, b - 1); 141 bytes = tmp; 142 } 143 return bytes; 144 } 145 else 146 { 147 throw new IOException("Error reading header"); 148 } 149 } 150 151 protected void sendMessageLength(int len) throws IOException { 152 serverOut.write (len >> 8); 153 serverOut.write (len); 154 serverOut.write (0); 155 serverOut.write (0); 156 } 157 /** 158 * @param m the message 159 * @param len already packed message len (to avoid re-pack) 160 * @exception IOException 161 */ 162 protected void sendMessageHeader(ISOMsg m, int len) 163 throws IOException 164 { 165 ISOHeader h = !isOverrideHeader() && m.getHeader() != null ? 166 m.getISOHeader() : 167 new BASE1Header (srcid, dstid, headerFormat); 168 169 if (h instanceof BASE1Header) 170 ((BASE1Header)h).setLen(len); 171 172 serverOut.write(h.pack()); 173 } 174 protected int getMessageLength() throws IOException, ISOException { 175 int l = 0; 176 byte[] b = new byte[4]; 177 // ignore VAP polls (0 message length) 178 while (l == 0) { 179 serverIn.readFully(b,0,4); 180 l = ((int)b[0] &0xFF) << 8 | (int)b[1] &0xFF; 181 182 if (replyKeepAlive && l == 0) { 183 try { 184 serverOutLock.lock(); 185 serverOut.write(b); 186 serverOut.flush(); 187 if (debugPoll) 188 Logger.log(new LogEvent(this, "poll")); 189 } finally { 190 serverOutLock.unlock(); 191 } 192 } 193 } 194 return l; 195 } 196 197 protected int getHeaderLength() { 198 return BASE1Header.LENGTH; 199 } 200 201 protected boolean isRejected(byte[] b) { 202 BASE1Header h = new BASE1Header(b); 203 return h.isRejected() || h.getHLen() != BASE1Header.LENGTH; 204 } 205 206 protected boolean shouldIgnore (byte[] b) { 207 if (b != null) { 208 BASE1Header h = new BASE1Header(b); 209 return h.getFormat() > 2; 210 } 211 return false; 212 } 213 214 215 /** 216 * sends an ISOMsg over the TCP/IP session. 217 * 218 * swap source/destination addresses in BASE1Header if 219 * a reply message is detected.<br> 220 * Sending an incoming message is seen as a reply. 221 * 222 * @param m the Message to be sent 223 * @exception IOException 224 * @exception ISOException 225 * @see ISOChannel#send 226 */ 227 public void send (ISOMsg m) throws IOException, ISOException 228 { 229 if (m.isIncoming() && m.getHeader() != null && swapDirection) { 230 m.getISOHeader().swapDirection(); 231 } 232 super.send(m); 233 } 234 235 public void setConfiguration (Configuration cfg) 236 throws ConfigurationException 237 { 238 super.setConfiguration (cfg); 239 srcid = cfg.get ("srcid", "000000"); 240 dstid = cfg.get ("dstid", "000000"); 241 debugPoll = cfg.getBoolean("debug-poll", false); 242 headerFormat = cfg.getInt("header-format", 2); 243 replyKeepAlive = cfg.getBoolean("reply-keepalive", true); 244 swapDirection = cfg.getBoolean("swap-direction", true); 245 } 246}