All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jpos.iso.packager.GenericPackager Maven / Gradle / Ivy

Go to download

jPOS is an ISO-8583 based financial transaction library/framework that can be customized and extended in order to implement financial interchanges.

There is a newer version: 2.1.9
Show newest version
/*
 * jPOS Project [http://jpos.org]
 * Copyright (C) 2000-2016 Alejandro P. Revilla
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see .
 */

package org.jpos.iso.packager;

import org.jpos.core.Configurable;
import org.jpos.core.Configuration;
import org.jpos.core.ConfigurationException;
import org.jpos.iso.TaggedFieldPackager;
import org.jpos.iso.ISOBasePackager;
import org.jpos.iso.ISOException;
import org.jpos.iso.ISOFieldPackager;
import org.jpos.iso.ISOMsgFieldPackager;
import org.jpos.iso.ISOPackager;
import org.jpos.util.LogSource;
import org.jpos.util.Logger;
import org.xml.sax.Attributes;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Stack;
import java.util.TreeMap;


/**
 * 
 * GenericPackager uses an XML config file to describe the layout of an ISOMessage
 * The general format is as follows
 * <isopackager>
 *     <isofield 
 *         id="[field id]"
 *         name="[field name]"
 *         length="[max field length]"
 *         class="[org.jpos.iso.IF_*]"
 *         pad="true|false">  
 *     </isofield>
 *     ...
 * </isopackager>
 *
 * Fields that contain subfields can be handled as follows
 * <isofieldpackager
 *     id="[field id]"
 *     name="[field name]"
 *     length="[field length]"
 *     class="[org.jpos.iso.IF_*]"
 *     packager="[org.jpos.iso.packager.*]">
 *      
 *     <isofield 
 *         id="[subfield id]"
 *         name="[subfield name]"
 *         length="[max subfield length]"
 *         class="[org.jpos.iso.IF_*]"
 *         pad="true|false">
 *     </isofield>
 *         ...
 * </isofieldpackager>
 *
 * The optional attributes maxValidField, bitmapField and emitBitmap
 * are allowed on the isopackager node.
 *
 * 
* @author Eoin Flood * @version $Revision$ $Date$ * @see ISOPackager * @see ISOBasePackager */ @SuppressWarnings("unchecked") public class GenericPackager extends ISOBasePackager implements Configurable { /* Values copied from ISOBasePackager These can be changes using attributes on the isopackager node */ private int maxValidField=128; private boolean emitBitmap=true; private int bitmapField=1; private String firstField = null; private String filename; public GenericPackager() throws ISOException { super(); } /** * Create a GenericPackager with the field descriptions * from an XML File * @param filename The XML field description file */ public GenericPackager(String filename) throws ISOException { this.filename = filename; readFile(filename); } /** * Create a GenericPackager with the field descriptions * from an XML InputStream * @param input The XML field description InputStream */ public GenericPackager(InputStream input) throws ISOException { readFile(input); } /** * Packager Configuration. * *
    *
  • packager-config *
  • packager-logger *
  • packager-realm *
* * @param cfg Configuration */ public void setConfiguration (Configuration cfg) throws ConfigurationException { filename = cfg.get("packager-config", null); if (filename == null) throw new ConfigurationException("packager-config property cannot be null"); try { String loggerName = cfg.get("packager-logger"); if (loggerName != null) setLogger(Logger.getLogger (loggerName), cfg.get ("packager-realm")); readFile(filename); } catch (ISOException e) { throw new ConfigurationException(e.getMessage(), e.fillInStackTrace()); } } @Override protected int getMaxValidField() { return maxValidField; } @Override protected boolean emitBitMap() { return emitBitmap; } @Override protected ISOFieldPackager getBitMapfieldPackager() { return fld[bitmapField]; } /** * Parse the field descriptions from an XML file. * *
     * Uses the sax parser specified by the system property 'sax.parser'
     * The default parser is org.apache.crimson.parser.XMLReaderImpl
     * 
* @param filename The XML field description file */ public void readFile(String filename) throws ISOException { try { if (filename.startsWith("jar:") && filename.length()>4) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); readFile( cl.getResourceAsStream(filename.substring(4)) ); } else { createXMLReader().parse(filename); } } catch (Exception e) { throw new ISOException("Error reading " + filename, e); } } /** * Parse the field descriptions from an XML InputStream. * *
     * Uses the sax parser specified by the system property 'sax.parser'
     * The default parser is org.apache.crimson.parser.XMLReaderImpl
     * 
* @param input The XML field description InputStream */ public void readFile(InputStream input) throws ISOException { try { createXMLReader().parse(new InputSource(input)); } catch (Exception e) { throw new ISOException(e); } } @Override public void setLogger (Logger logger, String realm) { super.setLogger (logger, realm); if (fld != null) { for (int i=0; i * The strategy we follow is:

* We first check whether the DTD points to a well defined URI, * and resolve to our internal DTDs.

* * If the systemId points to a file, then we attempt to read the * DTD from the filesystem, in case they've been modified by the user. * Otherwise, we fallback to the built-in DTDs inside jPOS.

* * @param publicId The public identifier of the external entity * being referenced, or null if none was supplied. * @param systemId The system identifier of the external entity * being referenced. * @return An InputSource object describing the new input source, * or null to request that the parser open a regular * URI connection to the system identifier. * @throws org.xml.sax.SAXException Any SAX exception, possibly * wrapping another exception. * @throws java.io.IOException A Java-specific IO exception, * possibly the result of creating a new InputStream * or Reader for the InputSource. * @see org.xml.sax.InputSource */ public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException { if(systemId==null) return null; ClassLoader cl =Thread.currentThread().getContextClassLoader(); cl = cl ==null?ClassLoader.getSystemClassLoader() : cl; if(systemId.equals("http://jpos.org/dtd/generic-packager-1.0.dtd")) { final URL resource = cl.getResource("org/jpos/iso/packager/genericpackager.dtd"); return new InputSource(resource.toExternalForm()); } else if(systemId.equals("http://jpos.org/dtd/generic-validating-packager-1.0.dtd")) { final URL resource = cl.getResource("org/jpos/iso/packager/generic-validating-packager.dtd"); return new InputSource(resource.toExternalForm()); } URL url=new URL(systemId); if(url.getProtocol().equals("file")) { String file=url.getFile(); if(file.endsWith(".dtd")) { File f=new File(file); InputStream res=null; if(f.exists()) { res=new FileInputStream(f); } if(res==null) { String dtdResource="org/jpos/iso/packager/"+f.getName(); res= cl.getResourceAsStream(dtdResource); } if(res!=null) return new InputSource(res); } } return null; } } public class GenericContentHandler extends DefaultHandler { private Stack fieldStack; @Override public void startDocument() { fieldStack = new Stack(); } @Override public void endDocument() throws SAXException { if (!fieldStack.isEmpty()) { throw new SAXException ("Format error in XML Field Description File"); } } @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { try { String id = atts.getValue("id"); String type = atts.getValue("class"); String name = atts.getValue("name"); String size = atts.getValue("length"); String pad = atts.getValue("pad"); // Modified for using TaggedFieldPackager String token = atts.getValue("token"); if (localName.equals("isopackager")) { // Stick a new Map on stack to collect the fields fieldStack.push(new TreeMap()); setGenericPackagerParams (atts); } if (localName.equals("isofieldpackager")) { /* For a isofield packager node push the following fields onto the stack. 1) an Integer indicating the field ID 2) an instance of the specified ISOFieldPackager class 3) an instance of the specified ISOBasePackager (msgPackager) class 4) a Map to collect the subfields */ String packager = atts.getValue("packager"); fieldStack.push(new Integer(id)); ISOFieldPackager f; f = (ISOFieldPackager) Class.forName(type).newInstance(); f.setDescription(name); f.setLength(Integer.parseInt(size)); f.setPad(Boolean.parseBoolean(pad)); // Modified for using TaggedFieldPackager if( f instanceof TaggedFieldPackager){ ((TaggedFieldPackager)f).setToken( token ); } fieldStack.push(f); ISOBasePackager p; p = (ISOBasePackager) Class.forName(packager).newInstance(); if (p instanceof GenericPackager) { GenericPackager gp = (GenericPackager) p; gp.setGenericPackagerParams (atts); } fieldStack.push(p); fieldStack.push(new TreeMap()); } if (localName.equals("isofield")) { Class c = Class.forName(type); ISOFieldPackager f; f = (ISOFieldPackager) c.newInstance(); f.setDescription(name); f.setLength(Integer.parseInt(size)); f.setPad(Boolean.parseBoolean(pad)); // Modified for using TaggedFieldPackager if( f instanceof TaggedFieldPackager){ ((TaggedFieldPackager)f).setToken( token ); } // Insert this new isofield into the Map // on the top of the stack using the fieldID as the key Map m = (Map) fieldStack.peek(); m.put(new Integer(id), f); } } catch (Exception ex) { throw new SAXException(ex); } } /** * Convert the ISOFieldPackagers in the Map * to an array of ISOFieldPackagers */ private ISOFieldPackager[] makeFieldArray(Map m) { int maxField = 0; // First find the largest field number in the Map for (Entry ent :m.entrySet()) if (ent.getKey() > maxField) maxField = ent.getKey(); // Create the array ISOFieldPackager fld[] = new ISOFieldPackager[maxField+1]; // Populate it for (Entry ent :m.entrySet()) fld[ent.getKey()] = ent.getValue(); return fld; } @Override public void endElement(String namespaceURI, String localName, String qName) { Map m; if (localName.equals("isopackager")) { m = (Map)fieldStack.pop(); setFieldPackager(makeFieldArray(m)); } if (localName.equals("isofieldpackager")) { // Pop the 4 entries off the stack in the correct order m = (Map)fieldStack.pop(); ISOBasePackager msgPackager = (ISOBasePackager) fieldStack.pop(); msgPackager.setFieldPackager (makeFieldArray(m)); ISOFieldPackager fieldPackager = (ISOFieldPackager) fieldStack.pop(); Integer fno = (Integer) fieldStack.pop(); msgPackager.setLogger (getLogger(), getRealm() + "-fld-" + fno); // Create the ISOMsgField packager with the retrieved msg and field Packagers ISOMsgFieldPackager mfp = new ISOMsgFieldPackager(fieldPackager, msgPackager); // Add the newly created ISOMsgField packager to the // lower level field stack m=(Map)fieldStack.peek(); m.put(fno, mfp); } } // ErrorHandler Methods @Override public void error (SAXParseException ex) throws SAXException { throw ex; } @Override public void fatalError (SAXParseException ex) throws SAXException { throw ex; } } @Override protected int getFirstField() { if (firstField != null) return Integer.parseInt (firstField); else return super.getFirstField(); } }