com.solab.iso8583.parse.ConfigParser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of j8583 Show documentation
Show all versions of j8583 Show documentation
Java implementation of the ISO 8583 protocol, focused on making the creation, edition and reading of
ISO8583 messages as simple and flexible as possible.
/*
j8583 A Java implementation of the ISO8583 protocol
Copyright (C) 2007 Enrique Zamudio Lopez
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package com.solab.iso8583.parse;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.net.URL;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.TimeZone;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import com.solab.iso8583.*;
import com.solab.iso8583.codecs.CompositeField;
import com.solab.iso8583.util.HexCodec;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import static com.solab.iso8583.IsoMessage.MAX_AMOUNT_OF_FIELDS;
/**
* This class is used to parse a XML configuration file and configure
* a MessageFactory with the values from it.
*
* @author Enrique Zamudio
*/
public class ConfigParser {
private static final Logger log = LoggerFactory.getLogger(ConfigParser.class);
/**
* Creates a message factory configured from the default file, which is j8583.xml
* located in the root of the classpath, using the specified ClassLoader.
*
* @param loader the loader
* @return the message factory
* @throws IOException the io exception
*/
public static MessageFactory createDefault(
ClassLoader loader) throws IOException {
if (loader.getResource("j8583.xml") == null) {
log.warn("ISO8583 ConfigParser cannot find j8583.xml, returning empty message factory");
return new MessageFactory<>();
} else {
return createFromClasspathConfig(loader, "j8583.xml");
}
}
/**
* Creates a message factory configured from the default file, which is j8583.xml
* located in the root of the classpath, using the MessageFactory's
* ClassLoader.
*
* @return the message factory
* @throws IOException the io exception
*/
public static MessageFactory createDefault() throws IOException {
return createDefault(MessageFactory.class.getClassLoader());
}
/**
* Creates a message factory from the specified path inside the classpath,
* using the specified ClassLoader.
*
* @param path the path
* @return the message factory
* @throws IOException the io exception
*/
public static MessageFactory createFromClasspathConfig(
String path) throws IOException {
return createFromClasspathConfig(MessageFactory.class.getClassLoader(), path);
}
/**
* Creates a message factory from the specified path inside the classpath,
* using MessageFactory's ClassLoader.
*
* @param loader the loader
* @param path the path
* @return the message factory
* @throws IOException the io exception
*/
public static MessageFactory createFromClasspathConfig(
ClassLoader loader, String path) throws IOException {
MessageFactory mfact = new MessageFactory<>();
try (InputStream ins = loader.getResourceAsStream(path)) {
if (ins != null) {
log.debug("ISO8583 Parsing config from classpath file {}", path);
parse(mfact, new InputSource(ins));
} else {
log.error("ISO8583 File not found in classpath: {}", path);
}
}
return mfact;
}
/**
* Creates a message factory from the file located at the specified URL.
*
* @param url the url
* @return the message factory
* @throws IOException the io exception
*/
public static MessageFactory createFromUrl(URL url) throws IOException {
MessageFactory mfact = new MessageFactory<>();
try (InputStream stream = url.openStream()) {
parse(mfact, new InputSource(stream));
}
return mfact;
}
/**
* Creates a messageFactory from the XML contained in the specified Reader.
*
* @param reader the reader
* @return the message factory
* @throws IOException the io exception
*/
public static MessageFactory createFromReader(Reader reader) throws IOException {
MessageFactory mfact = new MessageFactory<>();
parse(mfact, new InputSource(reader));
return mfact;
}
/**
* Parse headers.
*
* @param the type parameter
* @param nodes the nodes
* @param mfact the mfact
* @throws IOException the io exception
*/
protected static void parseHeaders(
final NodeList nodes, final MessageFactory mfact) throws IOException {
ArrayList refs = null;
for (int i = 0; i < nodes.getLength(); i++) {
Element elem = (Element)nodes.item(i);
int type = parseType(elem.getAttribute("type"));
if (type == -1) {
throw new IOException("Invalid type for ISO8583 header: " + elem.getAttribute("type"));
}
if (elem.getChildNodes() == null || elem.getChildNodes().getLength() == 0) {
if (elem.getAttribute("ref") != null && !elem.getAttribute("ref").isEmpty()) {
if (refs == null) {
refs = new ArrayList<>(nodes.getLength()-i);
}
refs.add(elem);
}
else throw new IOException("Invalid ISO8583 header element");
} else {
String header = elem.getChildNodes().item(0).getNodeValue();
boolean binHeader = "true".equals(elem.getAttribute("binary"));
if (log.isTraceEnabled()) {
log.trace("Adding {}ISO8583 header for type {}: {}",
binHeader?"binary ":"", elem.getAttribute("type"), header);
}
if (binHeader) {
mfact.setBinaryIsoHeader(type, HexCodec.hexDecode(header));
} else {
mfact.setIsoHeader(type, header);
}
}
}
if (refs != null) {
for (Element elem : refs) {
int type = parseType(elem.getAttribute("type"));
if (type == -1) {
throw new IOException("Invalid type for ISO8583 header: "
+ elem.getAttribute("type"));
}
if (elem.getAttribute("ref") != null && !elem.getAttribute("ref").isEmpty()) {
int t2 = parseType(elem.getAttribute("ref"));
if (t2 == -1) {
throw new IOException("Invalid type reference "
+ elem.getAttribute("ref") + " for ISO8583 header " + type);
}
String h = mfact.getIsoHeader(t2);
if (h == null) {
throw new IllegalArgumentException("Header def " + type +
" refers to nonexistent header " + t2);
}
if (log.isTraceEnabled()) {
log.trace("Adding ISO8583 header for type {}: {} (copied from {})",
elem.getAttribute("type"), h, elem.getAttribute("ref"));
}
mfact.setIsoHeader(type, h);
}
}
}
}
/**
* Parse templates.
*
* @param the type parameter
* @param nodes the nodes
* @param mfact the mfact
* @throws IOException the io exception
*/
protected static void parseTemplates(
final NodeList nodes, final MessageFactory mfact) throws IOException {
ArrayList subs = null;
for (int i = 0; i < nodes.getLength(); i++) {
Element elem = (Element)nodes.item(i);
int type = parseType(elem.getAttribute("type"));
if (type == -1) {
throw new IOException("Invalid ISO8583 type for template: " + elem.getAttribute("type"));
}
if (elem.getAttribute("extends") != null && !elem.getAttribute("extends").isEmpty()) {
if (subs == null) {
subs = new ArrayList<>(nodes.getLength()-i);
}
subs.add(elem);
continue;
}
@SuppressWarnings("unchecked")
T m = (T)new IsoMessage();
m.setType(type);
m.setCharacterEncoding(mfact.getCharacterEncoding());
NodeList fields = elem.getElementsByTagName("field");
for (int j = 0; j < fields.getLength(); j++) {
Element f = (Element)fields.item(j);
if (f.getParentNode()==elem) {
final int num = Integer.parseInt(f.getAttribute("num"));
IsoValue> v = getTemplateField(f, mfact, true);
if (v != null) {
v.setCharacterEncoding(mfact.getCharacterEncoding());
}
m.setField(num, v);
}
}
mfact.addMessageTemplate(m);
}
if (subs != null) {
for (Element elem : subs) {
int type = parseType(elem.getAttribute("type"));
int ref = parseType(elem.getAttribute("extends"));
if (ref == -1) {
throw new IllegalArgumentException("Message template "
+ elem.getAttribute("type") + " extends invalid template "
+ elem.getAttribute("extends"));
}
IsoMessage tref = mfact.getMessageTemplate(ref);
if (tref == null) {
throw new IllegalArgumentException("Message template "
+ elem.getAttribute("type") + " extends nonexistent template "
+ elem.getAttribute("extends"));
}
@SuppressWarnings("unchecked")
T m = (T)new IsoMessage();
m.setType(type);
m.setCharacterEncoding(mfact.getCharacterEncoding());
for (int i = 2; i <= MAX_AMOUNT_OF_FIELDS; i++) {
if (tref.hasField(i)) {
m.setField(i, new IsoValue<>(tref.getField(i)));
}
}
NodeList fields = elem.getElementsByTagName("field");
for (int j = 0; j < fields.getLength(); j++) {
Element f = (Element)fields.item(j);
int num = Integer.parseInt(f.getAttribute("num"));
if (f.getParentNode()==elem) {
IsoValue> v = getTemplateField(f, mfact, true);
if (v != null) {
v.setCharacterEncoding(mfact.getCharacterEncoding());
}
m.setField(num, v);
}
}
mfact.addMessageTemplate(m);
}
}
}
/**
* Creates an IsoValue from the XML definition in a message template.
* If it's for a toplevel field and the message factory has a codec for this field,
* that codec is assigned to that field. For nested fields, a CompositeField is
* created and populated.
*
* @param the type parameter
* @param f the f
* @param mfact the mfact
* @param toplevel the toplevel
* @return the template field
*/
protected static IsoValue> getTemplateField(
Element f, MessageFactory mfact, boolean toplevel) {
final int num = Integer.parseInt(f.getAttribute("num"));
final String typedef = f.getAttribute("type");
if ("exclude".equals(typedef)) {
return null;
}
int length = 0;
if (f.getAttribute("length").length() > 0) {
length = Integer.parseInt(f.getAttribute("length"));
}
final IsoType itype = IsoType.valueOf(typedef);
final NodeList subs = f.getElementsByTagName("field");
if (subs != null && subs.getLength() > 0) {
//Composite field
final CompositeField cf = new CompositeField();
for (int j = 0; j < subs.getLength(); j++) {
Element sub = (Element)subs.item(j);
if (sub.getParentNode()==f) {
IsoValue> sv = getTemplateField(sub, mfact, false);
if (sv != null) {
sv.setCharacterEncoding(mfact.getCharacterEncoding());
cf.addValue(sv);
}
}
}
IsoValue> rv = itype.needsLength() ? new IsoValue<>(itype, cf, length, cf) :
new IsoValue<>(itype, cf, cf);
if (f.hasAttribute("tz")) {
TimeZone tz = TimeZone.getTimeZone(f.getAttribute("tz"));
rv.setTimeZone(tz);
}
return rv;
}
final String v;
if (f.getChildNodes().getLength() == 0) {
v = "";
} else {
v = f.getChildNodes().item(0).getNodeValue();
}
final CustomField