From b1e09634d671cfc727c4c14023be67c54454a0c1 Mon Sep 17 00:00:00 2001 From: chochos Date: Mon, 29 Nov 2010 19:27:25 +0000 Subject: [PATCH] Made several changes that result in considerable performance improvements: - Fields inside the IsoMessage are now stored in an IsoValue[] instead of a map (this avoids the sorting step when encoding) - The constructor IsoValue(IsoType, T, int, CustomField) for LLVAR and LLLVAR types uses the received length parameter instead of reencoding the value with the CustomField; this results in much faster parsing performance. - FieldParseInfo passes the length of LLVAR and LLLVAR fields to the IsoValue constructor so that reencoding the value (or getting the length of the string) is not necessary. Field header length is checked to be digits when parsing ASCII. - IsoType now parses AMOUNT values as BigDecimals. - MessageFactory has separate code for parsing binary and ASCII messages. Also, the DTD was moved to the resources directory. git-svn-id: https://j8583.svn.sourceforge.net/svnroot/j8583/j8583@56 db60a039-f72e-0410-b1d2-baa000846441 --- .../java/com/solab/iso8583/IsoMessage.java | 50 ++++++--------- src/main/java/com/solab/iso8583/IsoType.java | 2 + src/main/java/com/solab/iso8583/IsoValue.java | 10 ++- .../com/solab/iso8583/MessageFactory.java | 63 ++++++++++++++----- .../solab/iso8583/parse/FieldParseInfo.java | 18 +++--- src/main/resources/PLACEHOLDER | 0 .../com/solab/iso8583/parse/j8583.dtd | 0 7 files changed, 87 insertions(+), 56 deletions(-) delete mode 100644 src/main/resources/PLACEHOLDER rename src/main/{java => resources}/com/solab/iso8583/parse/j8583.dtd (100%) diff --git a/src/main/java/com/solab/iso8583/IsoMessage.java b/src/main/java/com/solab/iso8583/IsoMessage.java index 0a6cf5a..062c0a7 100644 --- a/src/main/java/com/solab/iso8583/IsoMessage.java +++ b/src/main/java/com/solab/iso8583/IsoMessage.java @@ -22,11 +22,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; -import java.util.ArrayList; import java.util.BitSet; -import java.util.Collections; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; /** Represents an ISO8583 message. This is the core class of the framework. * Contains the bitmap which is modified as fields are added/removed. @@ -46,7 +42,7 @@ public class IsoMessage { /** Indicates if the message is binary-coded. */ private boolean binary; /** This is where the values are stored. */ - private Map> fields = new ConcurrentHashMap>(); + private IsoValue[] fields = new IsoValue[129]; /** Stores the optional ISO header. */ private String isoHeader; private int etx = -1; @@ -108,16 +104,13 @@ public class IsoMessage { * @param field The field number. 1 is the secondary bitmap and is not returned as such; * real fields go from 2 to 128. */ public Object getObjectValue(int field) { - IsoValue v = fields.get(field); - if (v == null) { - return null; - } - return v.getValue(); + IsoValue v = fields[field]; + return v == null ? null : v.getValue(); } /** Returns the IsoValue for the specified field. First real field is 2. */ public IsoValue getField(int field) { - return fields.get(field); + return fields[field]; } /** Stored the field in the specified index. The first field is the secondary bitmap and has index 1, @@ -126,11 +119,7 @@ public class IsoMessage { if (index < 2 || index > 128) { throw new IndexOutOfBoundsException("Field index must be between 2 and 128"); } - if (field == null) { - fields.remove(index); - } else { - fields.put(index, field); - } + fields[index] = field; } /** Sets the specified value in the specified field, creating an IsoValue internally. @@ -156,7 +145,7 @@ public class IsoMessage { throw new IndexOutOfBoundsException("Field index must be between 2 and 128"); } if (value == null) { - fields.remove(index); + fields[index] = null; } else { IsoValue v = null; if (t.needsLength()) { @@ -164,14 +153,14 @@ public class IsoMessage { } else { v = new IsoValue(t, value, encoder); } - fields.put(index, v); + fields[index] = v; } } /** Returns true is the message has a value in the specified field. * @param idx The field number. */ public boolean hasField(int idx) { - return fields.get(idx) != null; + return fields[idx] != null; } /** Writes a message to a stream, after writing the specified number of bytes indicating @@ -289,12 +278,11 @@ public class IsoMessage { } //Bitmap - ArrayList keys = new ArrayList(); - keys.addAll(fields.keySet()); - Collections.sort(keys); BitSet bs = new BitSet(forceb2 ? 128 : 64); - for (Integer i : keys) { - bs.set(i - 1); + for (int i = 2 ; i < 129; i++) { + if (fields[i] != null) { + bs.set(i - 1); + } } if (forceb2) { bs.set(0); @@ -338,12 +326,14 @@ public class IsoMessage { } //Fields - for (Integer i : keys) { - IsoValue v = fields.get(i); - try { - v.write(bout, binary); - } catch (IOException ex) { - //should never happen, writing to a ByteArrayOutputStream + for (int i = 2; i < 129; i++) { + IsoValue v = fields[i]; + if (v != null) { + try { + v.write(bout, binary); + } catch (IOException ex) { + //should never happen, writing to a ByteArrayOutputStream + } } } return bout.toByteArray(); diff --git a/src/main/java/com/solab/iso8583/IsoType.java b/src/main/java/com/solab/iso8583/IsoType.java index 133d5ff..a66a5da 100644 --- a/src/main/java/com/solab/iso8583/IsoType.java +++ b/src/main/java/com/solab/iso8583/IsoType.java @@ -114,6 +114,8 @@ public enum IsoType { } System.arraycopy(x, 0, c, lim, x.length); return new String(c); + } else if (this == AMOUNT) { + return IsoType.NUMERIC.format(new BigDecimal(value).movePointRight(2).intValue(), 12); } throw new IllegalArgumentException("Cannot format String as " + this); } diff --git a/src/main/java/com/solab/iso8583/IsoValue.java b/src/main/java/com/solab/iso8583/IsoValue.java index 96ef805..add7791 100644 --- a/src/main/java/com/solab/iso8583/IsoValue.java +++ b/src/main/java/com/solab/iso8583/IsoValue.java @@ -96,7 +96,9 @@ public class IsoValue implements Cloneable { if (length == 0 && t.needsLength()) { throw new IllegalArgumentException(String.format("Length must be greater than zero for type %s (value '%s')", t, val)); } else if (t == IsoType.LLVAR || t == IsoType.LLLVAR) { - length = custom == null ? val.toString().length() : custom.encodeField(value).length(); + if (len == 0) { + length = custom == null ? val.toString().length() : custom.encodeField(value).length(); + } if (t == IsoType.LLVAR && length > 99) { throw new IllegalArgumentException("LLVAR can only hold values up to 99 chars"); } else if (t == IsoType.LLLVAR && length > 999) { @@ -130,7 +132,11 @@ public class IsoValue implements Cloneable { } if (type == IsoType.NUMERIC || type == IsoType.AMOUNT) { if (type == IsoType.AMOUNT) { - return type.format((BigDecimal)value, 12); + if (value instanceof BigDecimal) { + return type.format((BigDecimal)value, 12); + } else { + return type.format(value.toString(), 12); + } } else if (value instanceof Number) { return type.format(((Number)value).longValue(), length); } else { diff --git a/src/main/java/com/solab/iso8583/MessageFactory.java b/src/main/java/com/solab/iso8583/MessageFactory.java index 4e8ec8f..c3bdc0d 100644 --- a/src/main/java/com/solab/iso8583/MessageFactory.java +++ b/src/main/java/com/solab/iso8583/MessageFactory.java @@ -313,25 +313,46 @@ public class MessageFactory { return m; } //Now we parse each field - for (Integer i : index) { - FieldParseInfo fpi = parseGuide.get(i); - if (bs.get(i - 1)) { - if (ignoreLast && pos >= buf.length && i == index.get(index.size() -1)) { - log.warn("Field {} is not really in the message even though it's in the bitmap", i); - bs.clear(i - 1); - } else { - IsoValue val = useBinary ? fpi.parseBinary(buf, pos, getCustomField(i)) : fpi.parse(buf, pos, getCustomField(i)); - m.setField(i, val); - if (useBinary && !(val.getType() == IsoType.ALPHA || val.getType() == IsoType.LLVAR - || val.getType() == IsoType.LLLVAR)) { - pos += (val.getLength() / 2) + (val.getLength() % 2); + if (useBinary) { + for (Integer i : index) { + FieldParseInfo fpi = parseGuide.get(i); + if (bs.get(i - 1)) { + if (ignoreLast && pos >= buf.length && i == index.get(index.size() -1)) { + log.warn("Field {} is not really in the message even though it's in the bitmap", i); + bs.clear(i - 1); } else { - pos += val.getLength(); + IsoValue val = fpi.parseBinary(buf, pos, getCustomField(i)); + m.setField(i, val); + if (!(val.getType() == IsoType.ALPHA || val.getType() == IsoType.LLVAR + || val.getType() == IsoType.LLLVAR)) { + pos += (val.getLength() / 2) + (val.getLength() % 2); + } else { + pos += val.getLength(); + } + if (val.getType() == IsoType.LLVAR) { + pos++; + } else if (val.getType() == IsoType.LLLVAR) { + pos += 2; + } } - if (val.getType() == IsoType.LLVAR) { - pos += useBinary ? 1 : 2; - } else if (val.getType() == IsoType.LLLVAR) { - pos += useBinary ? 2 : 3; + } + } + } else { + for (Integer i : index) { + FieldParseInfo fpi = parseGuide.get(i); + if (bs.get(i - 1)) { + if (ignoreLast && pos >= buf.length && i == index.get(index.size() -1)) { + log.warn("Field {} is not really in the message even though it's in the bitmap", i); + bs.clear(i - 1); + } else { + IsoValue val = fpi.parse(buf, pos, getCustomField(i)); + m.setField(i, val); + pos += val.getLength(); + if (val.getType() == IsoType.LLVAR) { + pos += 2; + } else if (val.getType() == IsoType.LLLVAR) { + pos += 3; + } } } } @@ -404,6 +425,14 @@ public class MessageFactory { return typeTemplates.get(type); } + public void freeze() { + typeTemplates = Collections.unmodifiableMap(typeTemplates); + parseMap = Collections.unmodifiableMap(parseMap); + parseOrder = Collections.unmodifiableMap(parseOrder); + isoHeaders = Collections.unmodifiableMap(isoHeaders); + customFields = Collections.unmodifiableMap(customFields); + } + /** Sets a map with the fields that are to be expected when parsing a certain type of * message. * @param type The message type. diff --git a/src/main/java/com/solab/iso8583/parse/FieldParseInfo.java b/src/main/java/com/solab/iso8583/parse/FieldParseInfo.java index 0326c4c..4233fd4 100644 --- a/src/main/java/com/solab/iso8583/parse/FieldParseInfo.java +++ b/src/main/java/com/solab/iso8583/parse/FieldParseInfo.java @@ -88,29 +88,33 @@ public class FieldParseInfo { throw new ParseException(String.format("Insufficient data for LLVAR field, pos %d", pos), pos); } if (custom == null) { - return new IsoValue(type, new String(buf, pos + 2, length), null); + return new IsoValue(type, new String(buf, pos + 2, length), length, null); } else { - IsoValue v = new IsoValue(type, custom.decodeField(new String(buf, pos + 2, length)), custom); + IsoValue v = new IsoValue(type, custom.decodeField(new String(buf, pos + 2, length)), length, custom); if (v.getValue() == null) { - return new IsoValue(type, new String(buf, pos + 2, length), null); + return new IsoValue(type, new String(buf, pos + 2, length), length, null); } return v; } } else if (type == IsoType.LLLVAR) { + if (!(Character.isDigit(buf[pos]) && Character.isDigit(buf[pos+1]) && Character.isDigit(buf[pos+2]))) { + throw new ParseException(String.format("Invalid LLLVAR length '%s' pos %d", new String(buf, pos, 3), pos), pos); + } length = ((buf[pos] - 48) * 100) + ((buf[pos + 1] - 48) * 10) + (buf[pos + 2] - 48); if (length < 0) { throw new ParseException(String.format("Invalid LLLVAR length %d pos %d", length, pos), pos); } - if (pos+3 > buf.length || length+pos+3 > buf.length) { + if (length+pos+3 > buf.length) { throw new ParseException(String.format("Insufficient data for LLLVAR field, pos %d", pos), pos); } + String _v = length == 0 ? "" : new String(buf, pos + 3, length); if (custom == null) { - return new IsoValue(type, new String(buf, pos + 3, length), null); + return new IsoValue(type, _v, length, null); } else { - IsoValue v = new IsoValue(type, custom.decodeField(new String(buf, pos + 3, length)), custom); + IsoValue v = new IsoValue(type, custom.decodeField(_v), length, custom); if (v.getValue() == null) { //problems decoding? return the string - return new IsoValue(type, new String(buf, pos + 3, length), null); + return new IsoValue(type, _v, length, null); } return v; } diff --git a/src/main/resources/PLACEHOLDER b/src/main/resources/PLACEHOLDER deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/com/solab/iso8583/parse/j8583.dtd b/src/main/resources/com/solab/iso8583/parse/j8583.dtd similarity index 100% rename from src/main/java/com/solab/iso8583/parse/j8583.dtd rename to src/main/resources/com/solab/iso8583/parse/j8583.dtd