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    protected Logger logger = null;
049    protected String realm = null;
050    private ByteArrayOutputStream out;
051    private PrintStream p;
052    private XMLReader reader = null;
053    private Stack stk;
054
055    private Lock lock = new ReentrantLock();
056
057    public static final String LOG_TAG       = "log";
058    public static final String ISOMSG_TAG    = "isomsg";
059    public static final String ISOFIELD_TAG  = "field";
060    public static final String ID_ATTR       = "id";
061    public static final String VALUE_ATTR    = "value";
062    public static final String TYPE_ATTR     = "type";
063    public static final String TYPE_BINARY   = "binary";
064    public static final String TYPE_BITMAP   = "bitmap";
065
066    public LogPackager() throws ISOException {
067        super();
068        out = new ByteArrayOutputStream();
069        p   = new PrintStream(out);
070        stk = new Stack();
071        try {
072            reader = XMLReaderFactory.createXMLReader(
073                System.getProperty( "sax.parser",
074                                    "org.apache.crimson.parser.XMLReaderImpl")
075            );
076            reader.setFeature ("http://xml.org/sax/features/validation",false);
077            reader.setContentHandler(this);
078            reader.setErrorHandler(this);
079        } catch (Exception e) {
080            throw new ISOException (e.toString());
081        }
082    }
083    public byte[] pack (ISOComponent c) throws ISOException {
084        LogEvent evt = new LogEvent (this, "pack");
085        lock.lock();
086        try {
087            if (!(c instanceof ISOMsg))
088                throw new ISOException ("cannot pack "+c.getClass());
089            ISOMsg m = (ISOMsg) c;
090            byte[] b;
091            p.println ("<log>");
092            c.dump (p, " ");
093            p.println ("</log>");
094            b = out.toByteArray();
095            out.reset();
096            if (logger != null)
097                evt.addMessage (m);
098            return b;
099        } catch (ISOException e) {
100            evt.addMessage (e);
101            throw e;
102        } finally {
103            Logger.log(evt);
104            lock.unlock();
105        }
106    }
107
108    public int unpack (ISOComponent c, byte[] b)
109        throws ISOException
110    {
111        LogEvent evt = new LogEvent (this, "unpack");
112        lock.lock();
113        try {
114            if (!(c instanceof ISOMsg))
115                throw new ISOException 
116                    ("Can't call packager on non Composite");
117
118            while (!stk.empty())    // purge from possible previous error
119                stk.pop();
120
121            InputSource src = new InputSource (new ByteArrayInputStream(b));
122            reader.parse (src);
123            if (!stk.empty()) {
124                ISOMsg m = (ISOMsg) c;
125                m.merge ((ISOMsg) stk.pop());
126                if (logger != null)     
127                    evt.addMessage (m);
128            }
129        } catch (ISOException e) {
130            evt.addMessage (e);
131            // throw e;
132        } catch (IOException e) {
133            evt.addMessage (e);
134            // throw new ISOException (e.toString());
135        } catch (SAXException e) {
136            evt.addMessage (e);
137            // throw new ISOException (e.toString());
138        } finally {
139            Logger.log (evt);
140            lock.unlock();
141        }
142        return b.length;
143    }
144
145    public void unpack (ISOComponent c, InputStream in)
146        throws ISOException, IOException
147    {
148        LogEvent evt = new LogEvent (this, "unpack");
149        lock.lock();
150        try {
151            if (!(c instanceof ISOMsg))
152                throw new ISOException 
153                    ("Can't call packager on non Composite");
154
155            while (!stk.empty())    // purge from possible previous error
156                stk.pop();
157
158            reader.parse (new InputSource (in));
159            if (!stk.empty()) {
160                ISOMsg m = (ISOMsg) c;
161                m.merge ((ISOMsg) stk.pop());
162                if (logger != null)     
163                    evt.addMessage (m);
164            }
165        } catch (ISOException e) {
166            evt.addMessage (e);
167            // throw e;
168        } catch (IOException e) {
169            evt.addMessage (e);
170            // throw new ISOException (e.toString());
171        } catch (SAXException e) {
172            evt.addMessage (e);
173            // throw new ISOException (e.toString());
174        } finally {
175            Logger.log (evt);
176            lock.unlock();
177        }
178    }
179
180    public void startElement 
181        (String ns, String name, String qName, Attributes atts)
182        throws SAXException
183    {
184        int fieldNumber = -1;
185        try {
186            String id       = atts.getValue(ID_ATTR);
187            if (id != null) {
188                try {
189                    fieldNumber = Integer.parseInt (id);
190                } catch (NumberFormatException ex) { }
191            }
192            if (name.equals (ISOMSG_TAG)) {
193                if (fieldNumber >= 0) {
194                    if (stk.empty())
195                        throw new SAXException ("inner without outter");
196
197                    ISOMsg inner = new ISOMsg(fieldNumber);
198                    ((ISOMsg)stk.peek()).set (inner);
199                    stk.push (inner);
200                } else {
201                    stk.push (new ISOMsg(0));
202                }
203            } else if (name.equals (ISOFIELD_TAG)) {
204                ISOMsg m     = (ISOMsg) stk.peek();
205                String value = atts.getValue(VALUE_ATTR);
206                String type  = atts.getValue(TYPE_ATTR);
207                if (id == null || value == null)
208                    throw new SAXException ("invalid field");   
209                if (TYPE_BINARY.equals (type)) {
210                    m.set (new ISOBinaryField (
211                        fieldNumber, 
212                            ISOUtil.hex2byte (
213                                value.getBytes(), 0, value.length()/2
214                            )
215                        )
216                    );
217                }
218                else {
219                    m.set (new ISOField (fieldNumber, value));
220                }
221            }
222        } catch (ISOException e) {
223            throw new SAXException 
224                ("ISOException unpacking "+fieldNumber);
225        }
226    }
227
228    public void endElement (String ns, String name, String qname) 
229        throws SAXException
230    {
231        if (name.equals (ISOMSG_TAG)) {
232            ISOMsg m = (ISOMsg) stk.pop();
233            if (stk.empty())
234                stk.push (m); // push outter message
235        }
236    }
237
238    public String getFieldDescription(ISOComponent m, int fldNumber) {
239        return "<notavailable/>";
240    }
241    public String getDescription () {
242        return getClass().getName();
243    }    
244    public void setLogger (Logger logger, String realm) {
245        this.logger = logger;
246        this.realm  = realm;
247    }
248    public String getRealm () {
249        return realm;
250    }
251    public Logger getLogger() {
252        return logger;
253    }
254    public ISOMsg createISOMsg() {
255        return new ISOMsg();
256    }
257}
258