From 6dbd13ca740094532b3d047ca25b3501142a96ab Mon Sep 17 00:00:00 2001 From: Wojciech Zankowski Date: Fri, 26 Jul 2019 23:55:21 +0200 Subject: [PATCH] QFJ-976: GarbledMessage without MsgType - Prevent FieldNotFound in Acceptor --- .../src/main/java/quickfix/BooleanField.java | 8 +++ .../src/main/java/quickfix/BytesField.java | 15 +++--- .../src/main/java/quickfix/CharField.java | 8 +++ .../src/main/java/quickfix/DateField.java | 8 +++ .../src/main/java/quickfix/DecimalField.java | 8 +++ .../src/main/java/quickfix/DoubleField.java | 8 +++ .../src/main/java/quickfix/Field.java | 9 ++-- .../src/main/java/quickfix/FieldMap.java | 42 +++++++++------ .../src/main/java/quickfix/IntField.java | 8 +++ .../src/main/java/quickfix/StringField.java | 6 +++ .../src/main/java/quickfix/UtcDateField.java | 9 +++- .../main/java/quickfix/UtcDateOnlyField.java | 9 +++- .../src/main/java/quickfix/UtcTimeField.java | 10 +++- .../main/java/quickfix/UtcTimeOnlyField.java | 13 +++-- .../main/java/quickfix/UtcTimeStampField.java | 11 +++- .../src/test/java/quickfix/FieldMapTest.java | 54 +++++++++++++++---- .../src/test/java/quickfix/FieldTest.java | 2 +- 17 files changed, 181 insertions(+), 47 deletions(-) diff --git a/quickfixj-core/src/main/java/quickfix/BooleanField.java b/quickfixj-core/src/main/java/quickfix/BooleanField.java index 1094b25779..6bea88d0c2 100644 --- a/quickfixj-core/src/main/java/quickfix/BooleanField.java +++ b/quickfixj-core/src/main/java/quickfix/BooleanField.java @@ -19,6 +19,8 @@ package quickfix; +import quickfix.field.converter.BooleanConverter; + import java.lang.Boolean; public class BooleanField extends Field { @@ -54,4 +56,10 @@ public boolean valueEquals(Boolean value) { public boolean valueEquals(boolean value) { return getObject().equals(value); } + + @Override + public String convertToString() { + return BooleanConverter.convert(getValue()); + } + } diff --git a/quickfixj-core/src/main/java/quickfix/BytesField.java b/quickfixj-core/src/main/java/quickfix/BytesField.java index e014587299..5f2bd1cbe7 100644 --- a/quickfixj-core/src/main/java/quickfix/BytesField.java +++ b/quickfixj-core/src/main/java/quickfix/BytesField.java @@ -19,9 +19,7 @@ package quickfix; -import java.io.UnsupportedEncodingException; - -import org.quickfixj.QFJException; +import java.nio.charset.StandardCharsets; /** * BytesField enables better handling of binary data. With BytesFields binary data can @@ -47,11 +45,12 @@ public byte[] getValue() { @Override protected String objectAsString() { - try { - return new String(getObject(), "UTF8"); - } catch (UnsupportedEncodingException e) { - throw new QFJException(e); - } + return new String(getObject(), StandardCharsets.UTF_8); + } + + @Override + public String convertToString() { + return objectAsString(); } } diff --git a/quickfixj-core/src/main/java/quickfix/CharField.java b/quickfixj-core/src/main/java/quickfix/CharField.java index c1da3fa2f1..67b8b3b33f 100644 --- a/quickfixj-core/src/main/java/quickfix/CharField.java +++ b/quickfixj-core/src/main/java/quickfix/CharField.java @@ -19,6 +19,8 @@ package quickfix; +import quickfix.field.converter.CharConverter; + import java.lang.Character; /** @@ -57,4 +59,10 @@ public boolean valueEquals(Character value) { public boolean valueEquals(char value) { return getObject().equals(value); } + + @Override + public String convertToString() { + return CharConverter.convert(getValue()); + } + } diff --git a/quickfixj-core/src/main/java/quickfix/DateField.java b/quickfixj-core/src/main/java/quickfix/DateField.java index 7963963317..6b0c1d1276 100644 --- a/quickfixj-core/src/main/java/quickfix/DateField.java +++ b/quickfixj-core/src/main/java/quickfix/DateField.java @@ -19,6 +19,8 @@ package quickfix; +import quickfix.field.converter.UtcTimestampConverter; + import java.util.Date; /** @@ -45,4 +47,10 @@ public Date getValue() { public boolean valueEquals(Date value) { return getValue().equals(value); } + + @Override + public String convertToString() { + return UtcTimestampConverter.convert(getValue(), true); + } + } diff --git a/quickfixj-core/src/main/java/quickfix/DecimalField.java b/quickfixj-core/src/main/java/quickfix/DecimalField.java index 52c1163eb7..d7632223db 100644 --- a/quickfixj-core/src/main/java/quickfix/DecimalField.java +++ b/quickfixj-core/src/main/java/quickfix/DecimalField.java @@ -19,6 +19,8 @@ package quickfix; +import quickfix.field.converter.DecimalConverter; + import java.math.BigDecimal; /** @@ -68,4 +70,10 @@ public boolean valueEquals(BigDecimal value) { public boolean valueEquals(double value) { return getValue().compareTo(new BigDecimal(value)) == 0; } + + @Override + public String convertToString() { + return DecimalConverter.convert(getValue(), getPadding()); + } + } diff --git a/quickfixj-core/src/main/java/quickfix/DoubleField.java b/quickfixj-core/src/main/java/quickfix/DoubleField.java index 419b269ef0..7a4020a216 100644 --- a/quickfixj-core/src/main/java/quickfix/DoubleField.java +++ b/quickfixj-core/src/main/java/quickfix/DoubleField.java @@ -19,6 +19,8 @@ package quickfix; +import quickfix.field.converter.DoubleConverter; + /** * A double-values message field. */ @@ -78,4 +80,10 @@ private void checkForValidDouble(Double value) throws NumberFormatException { throw new NumberFormatException("Tried to set NaN or infinite value."); } } + + @Override + public String convertToString() { + return DoubleConverter.convert(getValue(), getPadding()); + } + } diff --git a/quickfixj-core/src/main/java/quickfix/Field.java b/quickfixj-core/src/main/java/quickfix/Field.java index 07fde13be3..6e68af35d7 100644 --- a/quickfixj-core/src/main/java/quickfix/Field.java +++ b/quickfixj-core/src/main/java/quickfix/Field.java @@ -24,11 +24,9 @@ import java.io.Serializable; /** - * Base class for FIX message fields. This class should be - * abstract but that would break compatibility with the QF JNI - * classes. + * Base class for FIX message fields. */ -public /*abstract*/ class Field implements Serializable { +public abstract class Field implements Serializable { static final long serialVersionUID = 7098326013456432197L; private int tag; private T object; @@ -152,4 +150,7 @@ public void setTag(int tag) { isCalculated = false; calculate(); } + + public abstract String convertToString(); + } diff --git a/quickfixj-core/src/main/java/quickfix/FieldMap.java b/quickfixj-core/src/main/java/quickfix/FieldMap.java index fc87e66c04..19b173ce0d 100644 --- a/quickfixj-core/src/main/java/quickfix/FieldMap.java +++ b/quickfixj-core/src/main/java/quickfix/FieldMap.java @@ -86,8 +86,8 @@ public void clear() { public void reset() { fields.clear(); - for(List groupList : groups.values()) { - for(Group group : groupList) + for (List groupList : groups.values()) { + for (Group group : groupList) group.reset(); } groups.clear(); @@ -194,7 +194,9 @@ public void setUtcTimeStamp(int field, LocalDateTime value) { } public void setUtcTimeStamp(int field, LocalDateTime value, boolean includeMilliseconds) { - setField(new StringField(field, UtcTimestampConverter.convert(value, includeMilliseconds ? UtcTimestampPrecision.MILLIS : UtcTimestampPrecision.SECONDS))); + setField(new StringField(field, UtcTimestampConverter.convert(value, includeMilliseconds ? + UtcTimestampPrecision.MILLIS : + UtcTimestampPrecision.SECONDS))); } public void setUtcTimeStamp(int field, LocalDateTime value, UtcTimestampPrecision precision) { @@ -206,7 +208,9 @@ public void setUtcTimeOnly(int field, LocalTime value) { } public void setUtcTimeOnly(int field, LocalTime value, boolean includeMilliseconds) { - setField(new StringField(field, UtcTimeOnlyConverter.convert(value, includeMilliseconds ? UtcTimestampPrecision.MILLIS : UtcTimestampPrecision.SECONDS))); + setField(new StringField(field, UtcTimeOnlyConverter.convert(value, includeMilliseconds ? + UtcTimestampPrecision.MILLIS : + UtcTimestampPrecision.SECONDS))); } public void setUtcTimeOnly(int field, LocalTime value, UtcTimestampPrecision precision) { @@ -319,6 +323,10 @@ public LocalDate getUtcDateOnly(int field) throws FieldNotFound { } public void setField(int key, Field field) { + setString(key, field.convertToString()); + } + + public void setField(int key, BytesField field) { fields.put(key, field); } @@ -509,7 +517,8 @@ && getGroupCount(tag) > 0) { } } - private static final boolean IS_STRING_EQUIVALENT = CharsetSupport.isStringEquivalent(CharsetSupport.getCharsetInstance()); + private static final boolean IS_STRING_EQUIVALENT = CharsetSupport + .isStringEquivalent(CharsetSupport.getCharsetInstance()); int calculateLength() { int result = 0; @@ -524,11 +533,14 @@ int calculateLength() { for (Entry> entry : groups.entrySet()) { final List groupList = entry.getValue(); if (!groupList.isEmpty()) { - if(IS_STRING_EQUIVALENT) { - result += getStringLength(entry.getKey()) + getStringLength(groupList.size()) + 2; + if (IS_STRING_EQUIVALENT) { + result += + getStringLength(entry.getKey()) + getStringLength(groupList.size()) + 2; } else { - result += MessageUtils.length(CharsetSupport.getCharsetInstance(), NumbersCache.get(entry.getKey())); - result += MessageUtils.length(CharsetSupport.getCharsetInstance(), NumbersCache.get(groupList.size())); + result += MessageUtils.length(CharsetSupport.getCharsetInstance(), + NumbersCache.get(entry.getKey())); + result += MessageUtils.length(CharsetSupport.getCharsetInstance(), + NumbersCache.get(groupList.size())); result += 2; } for (int i = 0; i < groupList.size(); i++) { @@ -540,9 +552,10 @@ int calculateLength() { } private static int getStringLength(int num) { - if(num == 0) + if (num == 0) { return 1; - return (int)(num > 0 ? Math.log10(num) + 1 : Math.log10(-num) + 2); + } + return (int) (num > 0 ? Math.log10(num) + 1 : Math.log10(-num) + 2); } int calculateChecksum() { @@ -556,12 +569,12 @@ int calculateChecksum() { for (Entry> entry : groups.entrySet()) { final List groupList = entry.getValue(); if (!groupList.isEmpty()) { - if(IS_STRING_EQUIVALENT) { + if (IS_STRING_EQUIVALENT) { String value = NumbersCache.get(entry.getKey()); - for (int i = value.length(); i-- != 0;) + for (int i = value.length(); i-- != 0; ) result += value.charAt(i); value = NumbersCache.get(groupList.size()); - for (int i = value.length(); i-- != 0;) + for (int i = value.length(); i-- != 0; ) result += value.charAt(i); result += '=' + 1; } else { @@ -694,5 +707,4 @@ public boolean hasGroup(Group group) { return hasGroup(group.getFieldTag()); } - } diff --git a/quickfixj-core/src/main/java/quickfix/IntField.java b/quickfixj-core/src/main/java/quickfix/IntField.java index ba60260d31..1bd89288cd 100644 --- a/quickfixj-core/src/main/java/quickfix/IntField.java +++ b/quickfixj-core/src/main/java/quickfix/IntField.java @@ -19,6 +19,8 @@ package quickfix; +import quickfix.field.converter.IntConverter; + import java.lang.Integer; /** @@ -57,4 +59,10 @@ public boolean valueEquals(Integer value) { public boolean valueEquals(int value) { return getObject().equals(value); } + + @Override + public String convertToString() { + return IntConverter.convert(getValue()); + } + } diff --git a/quickfixj-core/src/main/java/quickfix/StringField.java b/quickfixj-core/src/main/java/quickfix/StringField.java index b98dbab196..e9833bcd01 100644 --- a/quickfixj-core/src/main/java/quickfix/StringField.java +++ b/quickfixj-core/src/main/java/quickfix/StringField.java @@ -43,4 +43,10 @@ public String getValue() { public boolean valueEquals(String value) { return getValue().equals(value); } + + @Override + public String convertToString() { + return getValue(); + } + } diff --git a/quickfixj-core/src/main/java/quickfix/UtcDateField.java b/quickfixj-core/src/main/java/quickfix/UtcDateField.java index 83697c6b6c..51e205a472 100644 --- a/quickfixj-core/src/main/java/quickfix/UtcDateField.java +++ b/quickfixj-core/src/main/java/quickfix/UtcDateField.java @@ -19,6 +19,8 @@ package quickfix; +import quickfix.field.converter.UtcDateOnlyConverter; + import java.time.LocalDate; import java.time.ZoneOffset; @@ -46,5 +48,10 @@ public LocalDate getValue() { public boolean valueEquals(LocalDate value) { return getValue().equals(value); } - + + @Override + public String convertToString() { + return UtcDateOnlyConverter.convert(getValue()); + } + } diff --git a/quickfixj-core/src/main/java/quickfix/UtcDateOnlyField.java b/quickfixj-core/src/main/java/quickfix/UtcDateOnlyField.java index cc26ab9fb9..221f79f60c 100644 --- a/quickfixj-core/src/main/java/quickfix/UtcDateOnlyField.java +++ b/quickfixj-core/src/main/java/quickfix/UtcDateOnlyField.java @@ -19,6 +19,8 @@ package quickfix; +import quickfix.field.converter.UtcDateOnlyConverter; + import java.time.LocalDate; import java.time.ZoneOffset; @@ -34,7 +36,7 @@ public UtcDateOnlyField(int field) { protected UtcDateOnlyField(int field, LocalDate data) { super(field, data); } - + public void setValue(LocalDate value) { setObject(value); } @@ -47,4 +49,9 @@ public boolean valueEquals(LocalDate value) { return getValue().equals(value); } + @Override + public String convertToString() { + return UtcDateOnlyConverter.convert(getValue()); + } + } diff --git a/quickfixj-core/src/main/java/quickfix/UtcTimeField.java b/quickfixj-core/src/main/java/quickfix/UtcTimeField.java index 52096d446e..630c5e00e5 100644 --- a/quickfixj-core/src/main/java/quickfix/UtcTimeField.java +++ b/quickfixj-core/src/main/java/quickfix/UtcTimeField.java @@ -19,6 +19,8 @@ package quickfix; +import quickfix.field.converter.UtcTimeOnlyConverter; + import java.time.LocalTime; import java.time.ZoneOffset; @@ -53,8 +55,14 @@ public LocalTime getValue() { public boolean valueEquals(LocalTime value) { return getValue().equals(value); } - + public UtcTimestampPrecision getPrecision() { return precision; } + + @Override + public String convertToString() { + return UtcTimeOnlyConverter.convert(getValue(), getPrecision()); + } + } diff --git a/quickfixj-core/src/main/java/quickfix/UtcTimeOnlyField.java b/quickfixj-core/src/main/java/quickfix/UtcTimeOnlyField.java index d45e201cdd..8fbc5c4fe1 100644 --- a/quickfixj-core/src/main/java/quickfix/UtcTimeOnlyField.java +++ b/quickfixj-core/src/main/java/quickfix/UtcTimeOnlyField.java @@ -19,6 +19,8 @@ package quickfix; +import quickfix.field.converter.UtcTimeOnlyConverter; + import java.time.LocalTime; import java.time.ZoneOffset; @@ -26,7 +28,7 @@ * A time-valued message field. */ public class UtcTimeOnlyField extends Field { - + private UtcTimestampPrecision precision = UtcTimestampPrecision.MILLIS; public UtcTimeOnlyField(int field) { @@ -51,11 +53,11 @@ protected UtcTimeOnlyField(int field, LocalTime data, boolean includeMillisecond super(field, data); this.precision = includeMilliseconds ? UtcTimestampPrecision.MILLIS : UtcTimestampPrecision.SECONDS; } - + public UtcTimestampPrecision getPrecision() { return precision; } - + public void setValue(LocalTime value) { setObject(value); } @@ -68,4 +70,9 @@ public boolean valueEquals(LocalTime value) { return getValue().equals(value); } + @Override + public String convertToString() { + return UtcTimeOnlyConverter.convert(getValue(), getPrecision()); + } + } diff --git a/quickfixj-core/src/main/java/quickfix/UtcTimeStampField.java b/quickfixj-core/src/main/java/quickfix/UtcTimeStampField.java index e100166730..ceaaebfb51 100644 --- a/quickfixj-core/src/main/java/quickfix/UtcTimeStampField.java +++ b/quickfixj-core/src/main/java/quickfix/UtcTimeStampField.java @@ -19,6 +19,8 @@ package quickfix; +import quickfix.field.converter.UtcTimestampConverter; + import java.time.LocalDateTime; import java.time.ZoneOffset; @@ -56,11 +58,11 @@ protected UtcTimeStampField(int field, LocalDateTime data, boolean includeMillis super(field, data); this.precision = includeMilliseconds ? UtcTimestampPrecision.MILLIS : UtcTimestampPrecision.SECONDS; } - + public UtcTimestampPrecision getPrecision() { return precision; } - + public void setValue(LocalDateTime value) { setObject(value); } @@ -73,4 +75,9 @@ public boolean valueEquals(LocalDateTime value) { return getValue().equals(value); } + @Override + public String convertToString() { + return UtcTimestampConverter.convert(getValue(), getPrecision()); + } + } diff --git a/quickfixj-core/src/test/java/quickfix/FieldMapTest.java b/quickfixj-core/src/test/java/quickfix/FieldMapTest.java index 9eb376a5c0..4310d83426 100644 --- a/quickfixj-core/src/test/java/quickfix/FieldMapTest.java +++ b/quickfixj-core/src/test/java/quickfix/FieldMapTest.java @@ -1,9 +1,5 @@ package quickfix; -import java.math.BigDecimal; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.ZoneOffset; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; @@ -11,9 +7,16 @@ import quickfix.field.MDEntryTime; import quickfix.field.converter.UtcTimeOnlyConverter; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneOffset; import java.util.Iterator; import java.util.Optional; +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; + /** * Tests the {@link FieldMap} class. * Specifically, verifies that the setters for {@link UtcTimeStampField} work correctly. @@ -34,7 +37,8 @@ public void testSetUtcTimeStampField() throws Exception { FieldMap map = new Message(); LocalDateTime aDate = LocalDateTime.now(); map.setField(new UtcTimeStampField(EffectiveTime.FIELD, aDate, false)); - assertEquals("milliseconds should not be preserved", epochMilliOfLocalDate(aDate) - (epochMilliOfLocalDate(aDate) % 1000), + assertEquals("milliseconds should not be preserved", + epochMilliOfLocalDate(aDate) - (epochMilliOfLocalDate(aDate) % 1000), epochMilliOfLocalDate(map.getField(new EffectiveTime()).getValue())); // now set it with preserving millis @@ -47,13 +51,17 @@ public void testSetUtcTimeOnlyField() throws Exception { FieldMap map = new Message(); LocalTime aDate = LocalTime.now(); map.setField(new UtcTimeOnlyField(MDEntryTime.FIELD, aDate, false)); - assertEquals("milliseconds should not be preserved", UtcTimeOnlyConverter.convert(aDate, UtcTimestampPrecision.SECONDS), - UtcTimeOnlyConverter.convert(map.getField(new MDEntryTime()).getValue(), UtcTimestampPrecision.SECONDS)); + assertEquals("milliseconds should not be preserved", + UtcTimeOnlyConverter.convert(aDate, UtcTimestampPrecision.SECONDS), + UtcTimeOnlyConverter.convert(map.getField(new MDEntryTime()).getValue(), + UtcTimestampPrecision.SECONDS)); // now set it with preserving millis map.setField(new UtcTimeOnlyField(MDEntryTime.FIELD, aDate, true)); - assertEquals("milliseconds should be preserved", UtcTimeOnlyConverter.convert(aDate, UtcTimestampPrecision.MILLIS), - UtcTimeOnlyConverter.convert(map.getField(new MDEntryTime()).getValue(), UtcTimestampPrecision.MILLIS)); + assertEquals("milliseconds should be preserved", + UtcTimeOnlyConverter.convert(aDate, UtcTimestampPrecision.MILLIS), + UtcTimeOnlyConverter.convert(map.getField(new MDEntryTime()).getValue(), + UtcTimestampPrecision.MILLIS)); } /** @@ -67,8 +75,10 @@ public void testSpecificFields() throws Exception { epochMilliOfLocalDate(map.getField(new EffectiveTime()).getValue())); LocalTime aTime = LocalTime.now(); map.setField(new MDEntryTime(aTime)); - assertEquals("milliseconds should be preserved", UtcTimeOnlyConverter.convert(aTime, UtcTimestampPrecision.MILLIS), - UtcTimeOnlyConverter.convert(map.getField(new MDEntryTime()).getValue(), UtcTimestampPrecision.MILLIS)); + assertEquals("milliseconds should be preserved", + UtcTimeOnlyConverter.convert(aTime, UtcTimestampPrecision.MILLIS), + UtcTimeOnlyConverter.convert(map.getField(new MDEntryTime()).getValue(), + UtcTimestampPrecision.MILLIS)); } private void testOrdering(int[] vals, int[] order, int[] expected) { @@ -114,4 +124,26 @@ public void testOptionalDecimal() { private long epochMilliOfLocalDate(LocalDateTime localDateTime) { return localDateTime.toInstant(ZoneOffset.UTC).toEpochMilli(); } + + // QFJ-962 + public void testSetFieldWithBytesField() throws FieldNotFound { + final BytesField bytesField = new BytesField(1111, new byte[] { 1, 2, 3, 4 }); + FieldMap map = new Message(); + map.setField(bytesField.getTag(), bytesField); + + final BytesField returnedField = map.getField(bytesField); + + assertEquals(bytesField, returnedField); + } + + // QFJ-962 + public void testSetFieldWithDecimalField() { + final DecimalField decimalField = new DecimalField(1111, new BigDecimal("0.00000000000001")); + + FieldMap map = new Message(); + map.setField(decimalField.getTag(), decimalField); + + assertThat(map.toString(), containsString("1111=0.00000000000001")); + } + } diff --git a/quickfixj-core/src/test/java/quickfix/FieldTest.java b/quickfixj-core/src/test/java/quickfix/FieldTest.java index 3bc5202361..d3cb991245 100644 --- a/quickfixj-core/src/test/java/quickfix/FieldTest.java +++ b/quickfixj-core/src/test/java/quickfix/FieldTest.java @@ -53,7 +53,7 @@ public void testMessageSetGetString() { } private void testFieldCalcuations(String value, int checksum, int length) { - Field field = new Field<>(12, value); + Field field = new StringField(12, value); field.setObject(value); assertEquals("12=" + value, field.toString()); assertEquals(checksum, field.getChecksum());