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.packager; 020 021import org.jpos.iso.*; 022import org.jpos.util.LogEvent; 023import org.jpos.util.LogSource; 024import org.jpos.util.Logger; 025import org.xml.sax.Attributes; 026import org.xml.sax.InputSource; 027import org.xml.sax.SAXException; 028import org.xml.sax.XMLReader; 029import org.xml.sax.helpers.DefaultHandler; 030import org.xml.sax.helpers.XMLReaderFactory; 031 032import java.io.*; 033import java.util.Stack; 034import java.util.concurrent.locks.Lock; 035import java.util.concurrent.locks.ReentrantLock; 036 037/** 038 * packs/unpacks ISOMsgs from jPOS logs 039 * 040 * @author apr@cs.com.uy 041 * @version $Id$ 042 * @see ISOPackager 043 */ 044@SuppressWarnings("unchecked") 045public class LogPackager extends DefaultHandler 046 implements ISOPackager, LogSource 047{ 048 /** Logger receiving pack/unpack diagnostic events. */ 049 protected Logger logger = null; 050 /** Logger realm associated with this packager. */ 051 protected String realm = null; 052 private ByteArrayOutputStream out; 053 private PrintStream p; 054 private XMLReader reader = null; 055 private Stack stk; 056 057 private Lock lock = new ReentrantLock(); 058 059 /** XML element name for the outer log wrapper. */ 060 public static final String LOG_TAG = "log"; 061 /** XML element name for an ISO message. */ 062 public static final String ISOMSG_TAG = "isomsg"; 063 /** XML element name for an ISO field. */ 064 public static final String ISOFIELD_TAG = "field"; 065 /** XML attribute carrying the field number. */ 066 public static final String ID_ATTR = "id"; 067 /** XML attribute carrying the field value. */ 068 public static final String VALUE_ATTR = "value"; 069 /** XML attribute identifying the field type ({@link #TYPE_BINARY}, {@link #TYPE_BITMAP}). */ 070 public static final String TYPE_ATTR = "type"; 071 /** Field-type marker: hex-encoded binary value. */ 072 public static final String TYPE_BINARY = "binary"; 073 /** Field-type marker: bitmap value. */ 074 public static final String TYPE_BITMAP = "bitmap"; 075 076 /** 077 * Constructs the packager and prepares its underlying SAX parser. 078 * 079 * @throws ISOException if the configured SAX parser cannot be instantiated 080 */ 081 public LogPackager() throws ISOException { 082 super(); 083 out = new ByteArrayOutputStream(); 084 p = new PrintStream(out); 085 stk = new Stack(); 086 try { 087 reader = XMLReaderFactory.createXMLReader( 088 System.getProperty( "sax.parser", 089 "org.apache.crimson.parser.XMLReaderImpl") 090 ); 091 reader.setFeature ("http://xml.org/sax/features/validation",false); 092 reader.setContentHandler(this); 093 reader.setErrorHandler(this); 094 } catch (Exception e) { 095 throw new ISOException (e.toString()); 096 } 097 } 098 public byte[] pack (ISOComponent c) throws ISOException { 099 LogEvent evt = new LogEvent (this, "pack"); 100 lock.lock(); 101 try { 102 if (!(c instanceof ISOMsg)) 103 throw new ISOException ("cannot pack "+c.getClass()); 104 ISOMsg m = (ISOMsg) c; 105 byte[] b; 106 p.println ("<log>"); 107 c.dump (p, " "); 108 p.println ("</log>"); 109 b = out.toByteArray(); 110 out.reset(); 111 if (logger != null) 112 evt.addMessage (m); 113 return b; 114 } catch (ISOException e) { 115 evt.addMessage (e); 116 throw e; 117 } finally { 118 Logger.log(evt); 119 lock.unlock(); 120 } 121 } 122 123 public int unpack (ISOComponent c, byte[] b) 124 throws ISOException 125 { 126 LogEvent evt = new LogEvent (this, "unpack"); 127 lock.lock(); 128 try { 129 if (!(c instanceof ISOMsg)) 130 throw new ISOException 131 ("Can't call packager on non Composite"); 132 133 while (!stk.empty()) // purge from possible previous error 134 stk.pop(); 135 136 InputSource src = new InputSource (new ByteArrayInputStream(b)); 137 reader.parse (src); 138 if (!stk.empty()) { 139 ISOMsg m = (ISOMsg) c; 140 m.merge ((ISOMsg) stk.pop()); 141 if (logger != null) 142 evt.addMessage (m); 143 } 144 } catch (ISOException e) { 145 evt.addMessage (e); 146 // throw e; 147 } catch (IOException e) { 148 evt.addMessage (e); 149 // throw new ISOException (e.toString()); 150 } catch (SAXException e) { 151 evt.addMessage (e); 152 // throw new ISOException (e.toString()); 153 } finally { 154 Logger.log (evt); 155 lock.unlock(); 156 } 157 return b.length; 158 } 159 160 public void unpack (ISOComponent c, InputStream in) 161 throws ISOException, IOException 162 { 163 LogEvent evt = new LogEvent (this, "unpack"); 164 lock.lock(); 165 try { 166 if (!(c instanceof ISOMsg)) 167 throw new ISOException 168 ("Can't call packager on non Composite"); 169 170 while (!stk.empty()) // purge from possible previous error 171 stk.pop(); 172 173 reader.parse (new InputSource (in)); 174 if (!stk.empty()) { 175 ISOMsg m = (ISOMsg) c; 176 m.merge ((ISOMsg) stk.pop()); 177 if (logger != null) 178 evt.addMessage (m); 179 } 180 } catch (ISOException e) { 181 evt.addMessage (e); 182 // throw e; 183 } catch (IOException e) { 184 evt.addMessage (e); 185 // throw new ISOException (e.toString()); 186 } catch (SAXException e) { 187 evt.addMessage (e); 188 // throw new ISOException (e.toString()); 189 } finally { 190 Logger.log (evt); 191 lock.unlock(); 192 } 193 } 194 195 public void startElement 196 (String ns, String name, String qName, Attributes atts) 197 throws SAXException 198 { 199 int fieldNumber = -1; 200 try { 201 String id = atts.getValue(ID_ATTR); 202 if (id != null) { 203 try { 204 fieldNumber = Integer.parseInt (id); 205 } catch (NumberFormatException ex) { } 206 } 207 if (name.equals (ISOMSG_TAG)) { 208 if (fieldNumber >= 0) { 209 if (stk.empty()) 210 throw new SAXException ("inner without outter"); 211 212 ISOMsg inner = new ISOMsg(fieldNumber); 213 ((ISOMsg)stk.peek()).set (inner); 214 stk.push (inner); 215 } else { 216 stk.push (new ISOMsg(0)); 217 } 218 } else if (name.equals (ISOFIELD_TAG)) { 219 ISOMsg m = (ISOMsg) stk.peek(); 220 String value = atts.getValue(VALUE_ATTR); 221 String type = atts.getValue(TYPE_ATTR); 222 if (id == null || value == null) 223 throw new SAXException ("invalid field"); 224 if (TYPE_BINARY.equals (type)) { 225 m.set (new ISOBinaryField ( 226 fieldNumber, 227 ISOUtil.hex2byte ( 228 value.getBytes(), 0, value.length()/2 229 ) 230 ) 231 ); 232 } 233 else { 234 m.set (new ISOField (fieldNumber, value)); 235 } 236 } 237 } catch (ISOException e) { 238 throw new SAXException 239 ("ISOException unpacking "+fieldNumber); 240 } 241 } 242 243 public void endElement (String ns, String name, String qname) 244 throws SAXException 245 { 246 if (name.equals (ISOMSG_TAG)) { 247 ISOMsg m = (ISOMsg) stk.pop(); 248 if (stk.empty()) 249 stk.push (m); // push outter message 250 } 251 } 252 253 public String getFieldDescription(ISOComponent m, int fldNumber) { 254 return "<notavailable/>"; 255 } 256 public String getDescription () { 257 return getClass().getName(); 258 } 259 public void setLogger (Logger logger, String realm) { 260 this.logger = logger; 261 this.realm = realm; 262 } 263 public String getRealm () { 264 return realm; 265 } 266 public Logger getLogger() { 267 return logger; 268 } 269 public ISOMsg createISOMsg() { 270 return new ISOMsg(); 271 } 272} 273