diff --git a/quickfixj-base/src/main/java/quickfix/DataDictionary.java b/quickfixj-base/src/main/java/quickfix/DataDictionary.java index 05e62fe9c1..012c63ccf6 100644 --- a/quickfixj-base/src/main/java/quickfix/DataDictionary.java +++ b/quickfixj-base/src/main/java/quickfix/DataDictionary.java @@ -88,11 +88,6 @@ private static Supplier createDocumentBuilderFactorySupp } private boolean hasVersion = false; - private boolean checkFieldsOutOfOrder = true; - private boolean checkFieldsHaveValues = true; - private boolean checkUserDefinedFields = true; - private boolean checkUnorderedGroupFields = true; - private boolean allowUnknownMessageFields = false; private String beginString; private String fullVersion; private String majorVersion; @@ -532,86 +527,6 @@ private boolean isMultipleValueStringField(int field) { fieldType == FieldType.MULTIPLECHARVALUE; } - /** - * Controls whether out of order fields are checked. - * - * @param flag true = checked, false = not checked - */ - public void setCheckFieldsOutOfOrder(boolean flag) { - checkFieldsOutOfOrder = flag; - } - - public boolean isCheckFieldsOutOfOrder() { - return checkFieldsOutOfOrder; - } - - public boolean isCheckUnorderedGroupFields() { - return checkUnorderedGroupFields; - } - - public boolean isCheckFieldsHaveValues() { - return checkFieldsHaveValues; - } - - public boolean isCheckUserDefinedFields() { - return checkUserDefinedFields; - } - - public boolean isAllowUnknownMessageFields() { - return allowUnknownMessageFields; - } - - /** - * Controls whether group fields are in the same order - * - * @param flag true = checked, false = not checked - */ - public void setCheckUnorderedGroupFields(boolean flag) { - checkUnorderedGroupFields = flag; - for (Map gm : groups.values()) { - for (GroupInfo gi : gm.values()) { - gi.getDataDictionary().setCheckUnorderedGroupFields(flag); - } - } - } - - /** - * Controls whether empty field values are checked. - * - * @param flag true = checked, false = not checked - */ - public void setCheckFieldsHaveValues(boolean flag) { - checkFieldsHaveValues = flag; - for (Map gm : groups.values()) { - for (GroupInfo gi : gm.values()) { - gi.getDataDictionary().setCheckFieldsHaveValues(flag); - } - } - } - - /** - * Controls whether user defined fields are checked. - * - * @param flag true = checked, false = not checked - */ - public void setCheckUserDefinedFields(boolean flag) { - checkUserDefinedFields = flag; - for (Map gm : groups.values()) { - for (GroupInfo gi : gm.values()) { - gi.getDataDictionary().setCheckUserDefinedFields(flag); - } - } - } - - public void setAllowUnknownMessageFields(boolean allowUnknownFields) { - allowUnknownMessageFields = allowUnknownFields; - for (Map gm : groups.values()) { - for (GroupInfo gi : gm.values()) { - gi.getDataDictionary().setAllowUnknownMessageFields(allowUnknownFields); - } - } - } - private void copyFrom(DataDictionary rhs) { hasVersion = rhs.hasVersion; beginString = rhs.beginString; @@ -632,13 +547,6 @@ private void copyFrom(DataDictionary rhs) { copyMap(valueNames, rhs.valueNames); copyGroups(groups, rhs.groups); copyMap(components, rhs.components); - - setCheckFieldsOutOfOrder(rhs.checkFieldsOutOfOrder); - setCheckFieldsHaveValues(rhs.checkFieldsHaveValues); - setCheckUserDefinedFields(rhs.checkUserDefinedFields); - setCheckUnorderedGroupFields(rhs.checkUnorderedGroupFields); - setAllowUnknownMessageFields(rhs.allowUnknownMessageFields); - calculateOrderedFields(); } @@ -688,13 +596,14 @@ private static void copyCollection(Collection lhs, Collection rhs) { * Validate a message, including the header and trailer fields. * * @param message the message + * @param settings * @throws IncorrectTagValue if a field value is not valid * @throws FieldNotFound if a field cannot be found * @throws IncorrectDataFormat if a field value has a wrong data type */ - public void validate(Message message) throws IncorrectTagValue, FieldNotFound, + public void validate(Message message, ValidationSettings settings) throws IncorrectTagValue, FieldNotFound, IncorrectDataFormat { - validate(message, false); + validate(message, false, settings); } /** @@ -702,20 +611,24 @@ public void validate(Message message) throws IncorrectTagValue, FieldNotFound, * * @param message the message * @param bodyOnly whether to validate just the message body, or to validate the header and trailer sections as well. + * @param settings * @throws IncorrectTagValue if a field value is not valid * @throws FieldNotFound if a field cannot be found * @throws IncorrectDataFormat if a field value has a wrong data type */ - public void validate(Message message, boolean bodyOnly) throws IncorrectTagValue, + public void validate(Message message, boolean bodyOnly, ValidationSettings settings) throws IncorrectTagValue, FieldNotFound, IncorrectDataFormat { - validate(message, bodyOnly ? null : this, this); + validate(message, bodyOnly ? null : this, this, settings); } static void validate(Message message, DataDictionary sessionDataDictionary, - DataDictionary applicationDataDictionary) throws IncorrectTagValue, FieldNotFound, + DataDictionary applicationDataDictionary, ValidationSettings settings) throws IncorrectTagValue, FieldNotFound, IncorrectDataFormat { final boolean bodyOnly = sessionDataDictionary == null; - + if (settings == null) { + settings = new ValidationSettings(); + } + if (isVersionSpecified(sessionDataDictionary) && !sessionDataDictionary.getVersion().equals( message.getHeader().getString(BeginString.FIELD)) @@ -737,38 +650,38 @@ static void validate(Message message, DataDictionary sessionDataDictionary, } if (!bodyOnly) { - sessionDataDictionary.iterate(message.getHeader(), HEADER_ID, sessionDataDictionary); - sessionDataDictionary.iterate(message.getTrailer(), TRAILER_ID, sessionDataDictionary); + sessionDataDictionary.iterate(settings, message.getHeader(), HEADER_ID, sessionDataDictionary); + sessionDataDictionary.iterate(settings, message.getTrailer(), TRAILER_ID, sessionDataDictionary); } - applicationDataDictionary.iterate(message, msgType, applicationDataDictionary); + applicationDataDictionary.iterate(settings, message, msgType, applicationDataDictionary); } private static boolean isVersionSpecified(DataDictionary dd) { return dd != null && dd.hasVersion; } - private void iterate(FieldMap map, String msgType, DataDictionary dd) throws IncorrectTagValue, + private void iterate(ValidationSettings settings, FieldMap map, String msgType, DataDictionary dd) throws IncorrectTagValue, IncorrectDataFormat { for (final Field f : map) { final StringField field = (StringField) f; - checkHasValue(field); + checkHasValue(settings, field); if (hasVersion) { - checkValidFormat(field); + checkValidFormat(settings, field); checkValue(field); } if (beginString != null) { - dd.checkField(field, msgType, map instanceof Message); + dd.checkField(settings, field, msgType, map instanceof Message); dd.checkGroupCount(field, map, msgType); } } for (final List groups : map.getGroups().values()) { for (final Group group : groups) { - iterate(group, msgType, dd.getGroup(msgType, group.getFieldTag()) + iterate(settings, group, msgType, dd.getGroup(msgType, group.getFieldTag()) .getDataDictionary()); } } @@ -789,10 +702,10 @@ void checkValidTagNumber(Field field) { } /** Check if field tag is defined for message or group **/ - void checkField(Field field, String msgType, boolean message) { + void checkField(ValidationSettings settings, Field field, String msgType, boolean message) { // use different validation for groups and messages boolean messageField = message ? isMsgField(msgType, field.getField()) : fields.contains(field.getField()); - boolean fail = checkFieldFailure(field.getField(), messageField); + boolean fail = checkFieldFailure(settings, field.getField(), messageField); if (fail) { if (fields.contains(field.getField())) { @@ -803,22 +716,22 @@ void checkField(Field field, String msgType, boolean message) { } } - boolean checkFieldFailure(int field, boolean messageField) { + boolean checkFieldFailure(ValidationSettings settings, int field, boolean messageField) { boolean fail; if (field < USER_DEFINED_TAG_MIN) { - fail = !messageField && !allowUnknownMessageFields; + fail = !messageField && !settings.allowUnknownMessageFields; } else { - fail = !messageField && checkUserDefinedFields; + fail = !messageField && settings.checkUserDefinedFields; } return fail; } - private void checkValidFormat(StringField field) throws IncorrectDataFormat { + private void checkValidFormat(ValidationSettings settings, StringField field) throws IncorrectDataFormat { FieldType fieldType = getFieldType(field.getTag()); if (fieldType == null) { return; } - if (!checkFieldsHaveValues && field.getValue().length() == 0) { + if (!settings.checkFieldsHaveValues && field.getValue().length() == 0) { return; } try { @@ -883,8 +796,8 @@ private void checkValue(StringField field) throws IncorrectTagValue { } /** Check if a field has a value. **/ - private void checkHasValue(StringField field) { - if (checkFieldsHaveValues && field.getValue().length() == 0) { + private void checkHasValue(ValidationSettings settings, StringField field) { + if (settings.checkFieldsHaveValues && field.getValue().length() == 0) { throw new FieldException(SessionRejectReason.TAG_SPECIFIED_WITHOUT_A_VALUE, field.getField()); } diff --git a/quickfixj-base/src/main/java/quickfix/Message.java b/quickfixj-base/src/main/java/quickfix/Message.java index c974021e03..ae5d4cd240 100644 --- a/quickfixj-base/src/main/java/quickfix/Message.java +++ b/quickfixj-base/src/main/java/quickfix/Message.java @@ -91,27 +91,32 @@ protected Message(int[] fieldOrder) { public Message(String string) throws InvalidMessage { initializeHeader(); - fromString(string, null, true, true); + fromString(string, null, null, true, true); } public Message(String string, boolean validate) throws InvalidMessage { initializeHeader(); - fromString(string, null, validate, true); + fromString(string, null, null, validate, true); } public Message(String string, DataDictionary dd) throws InvalidMessage { initializeHeader(); - fromString(string, dd, true, true); + fromString(string, dd, new ValidationSettings(), true, true); } - public Message(String string, DataDictionary dd, boolean validate) throws InvalidMessage { + public Message(String string, DataDictionary dd, ValidationSettings dds) throws InvalidMessage { initializeHeader(); - fromString(string, dd, validate, true); + fromString(string, dd, dds, true, true); + } + + public Message(String string, DataDictionary dd, ValidationSettings dds, boolean validate) throws InvalidMessage { + initializeHeader(); + fromString(string, dd, dds, validate, true); } - public Message(String string, DataDictionary sessionDictionary, DataDictionary applicationDictionary, boolean validate) throws InvalidMessage { + public Message(String string, DataDictionary sessionDictionary, DataDictionary applicationDictionary, ValidationSettings validationSettings, boolean validate) throws InvalidMessage { initializeHeader(); - fromString(string, sessionDictionary, applicationDictionary, validate, true); + fromString(string, sessionDictionary, applicationDictionary, validationSettings, validate, true); } private void initializeHeader() { @@ -573,38 +578,41 @@ private void optionallySetID(Header header, int field, String value) { } } - public void fromString(String messageData, DataDictionary dd, boolean doValidation) + public void fromString(String messageData, DataDictionary dd, ValidationSettings validationSettings, boolean doValidation) throws InvalidMessage { - parse(messageData, dd, dd, doValidation, true); + parse(messageData, dd, dd, validationSettings, doValidation, true); } - public void fromString(String messageData, DataDictionary dd, boolean doValidation, - boolean validateChecksum) throws InvalidMessage { - parse(messageData, dd, dd, doValidation, validateChecksum); + public void fromString(String messageData, DataDictionary dd, ValidationSettings dds, boolean doValidation, + boolean validateChecksum) throws InvalidMessage { + parse(messageData, dd, dd, dds, doValidation, validateChecksum); } public void fromString(String messageData, DataDictionary sessionDictionary, - DataDictionary applicationDictionary, boolean doValidation) throws InvalidMessage { - fromString(messageData, sessionDictionary, applicationDictionary, doValidation, true); + DataDictionary applicationDictionary, ValidationSettings validationSettings, boolean doValidation) throws InvalidMessage { + fromString(messageData, sessionDictionary, applicationDictionary, validationSettings, doValidation, true); } public void fromString(String messageData, DataDictionary sessionDictionary, - DataDictionary applicationDictionary, boolean doValidation, boolean validateChecksum) + DataDictionary applicationDictionary, ValidationSettings validationSettings, boolean doValidation, boolean validateChecksum) throws InvalidMessage { if (sessionDictionary.isAdminMessage(MessageUtils.getMessageType(messageData))) { applicationDictionary = sessionDictionary; } - parse(messageData, sessionDictionary, applicationDictionary, doValidation, validateChecksum); + if (validationSettings == null) { + validationSettings = new ValidationSettings(); + } + parse(messageData, sessionDictionary, applicationDictionary, validationSettings, doValidation, validateChecksum); } void parse(String messageData, DataDictionary sessionDataDictionary, - DataDictionary applicationDataDictionary, boolean doValidation, - boolean validateChecksum) throws InvalidMessage { + DataDictionary applicationDataDictionary, ValidationSettings validationSettings, boolean doValidation, + boolean validateChecksum) throws InvalidMessage { this.messageData = messageData; try { - parseHeader(sessionDataDictionary, doValidation); - parseBody(sessionDataDictionary, applicationDataDictionary, doValidation); + parseHeader(sessionDataDictionary, validationSettings, doValidation); + parseBody(sessionDataDictionary, applicationDataDictionary, validationSettings, doValidation); parseTrailer(sessionDataDictionary); if (doValidation && validateChecksum) { validateCheckSum(messageData); @@ -628,7 +636,7 @@ private void validateCheckSum(String messageData) throws InvalidMessage { } } - private void parseHeader(DataDictionary dd, boolean doValidation) throws InvalidMessage { + private void parseHeader(DataDictionary dd, ValidationSettings dds, boolean doValidation) throws InvalidMessage { if (doValidation) { final boolean validHeaderFieldOrder = isNextField(dd, header, BeginString.FIELD) && isNextField(dd, header, BodyLength.FIELD) @@ -645,7 +653,7 @@ && isNextField(dd, header, BodyLength.FIELD) header.setField(field); if (dd != null && dd.isGroup(DataDictionary.HEADER_ID, field.getField())) { - parseGroup(DataDictionary.HEADER_ID, field, dd, dd, header, doValidation); + parseGroup(DataDictionary.HEADER_ID, field, dd, dd, dds, header, doValidation); } field = extractField(dd, header); @@ -670,7 +678,7 @@ private String getMsgType() throws InvalidMessage { } } - private void parseBody(DataDictionary sessionDataDictionary, DataDictionary applicationDataDictionary, boolean doValidation) throws InvalidMessage { + private void parseBody(DataDictionary sessionDataDictionary, DataDictionary applicationDataDictionary, ValidationSettings dds, boolean doValidation) throws InvalidMessage { StringField field = extractField(applicationDataDictionary, this); while (field != null) { if (isTrailerField(field.getField())) { @@ -684,16 +692,17 @@ private void parseBody(DataDictionary sessionDataDictionary, DataDictionary appl setField(header, field); // Group case if (sessionDataDictionary != null && sessionDataDictionary.isGroup(DataDictionary.HEADER_ID, field.getField())) { - parseGroup(DataDictionary.HEADER_ID, field, sessionDataDictionary, sessionDataDictionary, header, doValidation); + parseGroup(DataDictionary.HEADER_ID, field, sessionDataDictionary, sessionDataDictionary, dds, header, doValidation); } - if (doValidation && sessionDataDictionary != null && sessionDataDictionary.isCheckFieldsOutOfOrder()) + if (doValidation && sessionDataDictionary != null && dds.isCheckFieldsOutOfOrder()) { throw new FieldException(SessionRejectReason.TAG_SPECIFIED_OUT_OF_REQUIRED_ORDER, - field.getTag()); + field.getTag()); + } } else { setField(this, field); // Group case if (applicationDataDictionary != null && applicationDataDictionary.isGroup(getMsgType(), field.getField())) { - parseGroup(getMsgType(), field, applicationDataDictionary, applicationDataDictionary, this, doValidation); + parseGroup(getMsgType(), field, applicationDataDictionary, applicationDataDictionary, dds, this, doValidation); } } @@ -708,7 +717,7 @@ private void setField(FieldMap fields, StringField field) { fields.setField(field); } - private void parseGroup(String msgType, StringField field, DataDictionary dd, DataDictionary parentDD, FieldMap parent, boolean doValidation) + private void parseGroup(String msgType, StringField field, DataDictionary dd, DataDictionary parentDD, ValidationSettings dds, FieldMap parent, boolean doValidation) throws InvalidMessage { final DataDictionary.GroupInfo rg = dd.getGroup(msgType, field.getField()); final DataDictionary groupDataDictionary = rg.getDataDictionary(); @@ -740,18 +749,18 @@ private void parseGroup(String msgType, StringField field, DataDictionary dd, Da previousOffset = -1; // QFJ-742 if (groupDataDictionary.isGroup(msgType, tag)) { - parseGroup(msgType, field, groupDataDictionary, parentDD, group, doValidation); + parseGroup(msgType, field, groupDataDictionary, parentDD, dds, group, doValidation); } } else if (groupDataDictionary.isGroup(msgType, tag)) { if (group != null) { - parseGroup(msgType, field, groupDataDictionary, parentDD, group, doValidation); + parseGroup(msgType, field, groupDataDictionary, parentDD, dds, group, doValidation); } else { // QFJ-934: message should be rejected and not ignored when first field not found throw newFieldExceptionMissingDelimiter(groupCountTag, firstField, tag); } } else if (groupDataDictionary.isField(tag)) { if (group != null) { - if (fieldOrder != null && dd.isCheckUnorderedGroupFields()) { + if (fieldOrder != null && dds.isCheckUnorderedGroupFields()) { final int offset = indexOf(tag, fieldOrder); if (offset > -1) { if (offset <= previousOffset) { @@ -771,7 +780,7 @@ private void parseGroup(String msgType, StringField field, DataDictionary dd, Da } else { // QFJ-169/QFJ-791: handle unknown repeating group fields in the body if (!isTrailerField(tag) && !(DataDictionary.HEADER_ID.equals(msgType) || isHeaderField(field, dd))) { - if (checkFieldValidation(parent, parentDD, field, msgType, doValidation, group)) { + if (checkFieldValidation(parent, parentDD, dds, field, msgType, doValidation, group)) { continue; } } @@ -797,11 +806,11 @@ private FieldException newFieldExceptionMissingDelimiter(final int groupCountTag + " must set the delimiter field " + firstField, tag); } - private boolean checkFieldValidation(FieldMap parent, DataDictionary parentDD, StringField field, String msgType, boolean doValidation, Group group) throws FieldException { + private boolean checkFieldValidation(FieldMap parent, DataDictionary parentDD, ValidationSettings dds, StringField field, String msgType, boolean doValidation, Group group) throws FieldException { boolean isField = (parent instanceof Group) ? parentDD.isField(field.getTag()) : parentDD.isMsgField(msgType, field.getTag()); if (!isField) { if (doValidation) { - boolean fail = parentDD.checkFieldFailure(field.getTag(), false); + boolean fail = parentDD.checkFieldFailure(dds, field.getTag(), false); if (fail) { throw new FieldException(SessionRejectReason.TAG_NOT_DEFINED_FOR_THIS_MESSAGE_TYPE, field.getTag()); } diff --git a/quickfixj-base/src/main/java/quickfix/MessageUtils.java b/quickfixj-base/src/main/java/quickfix/MessageUtils.java index 75e7e09d85..a590aaf5e2 100644 --- a/quickfixj-base/src/main/java/quickfix/MessageUtils.java +++ b/quickfixj-base/src/main/java/quickfix/MessageUtils.java @@ -89,9 +89,9 @@ private static String getFieldOrDefault(FieldMap fields, int tag, String default } } - public static Message parse(MessageFactory messageFactory, DataDictionary dataDictionary, + public static Message parse(MessageFactory messageFactory, DataDictionary dataDictionary, ValidationSettings validationSettings, String messageString) throws InvalidMessage { - return parse(messageFactory, dataDictionary, messageString, true); + return parse(messageFactory, dataDictionary, validationSettings, messageString, true); } /** @@ -105,7 +105,7 @@ public static Message parse(MessageFactory messageFactory, DataDictionary dataDi * @return the parsed message * @throws InvalidMessage */ - public static Message parse(MessageFactory messageFactory, DataDictionary dataDictionary, + public static Message parse(MessageFactory messageFactory, DataDictionary dataDictionary, ValidationSettings validationSettings, String messageString, boolean validateChecksum) throws InvalidMessage { final int index = messageString.indexOf(FIELD_SEPARATOR); if (index < 0) { @@ -114,7 +114,7 @@ public static Message parse(MessageFactory messageFactory, DataDictionary dataDi final String beginString = messageString.substring(2, index); final String messageType = getMessageType(messageString); final quickfix.Message message = messageFactory.create(beginString, messageType); - message.fromString(messageString, dataDictionary, dataDictionary != null, validateChecksum); + message.fromString(messageString, dataDictionary, validationSettings, dataDictionary != null, validateChecksum); return message; } diff --git a/quickfixj-base/src/main/java/quickfix/ValidationSettings.java b/quickfixj-base/src/main/java/quickfix/ValidationSettings.java new file mode 100644 index 0000000000..f2d242eac6 --- /dev/null +++ b/quickfixj-base/src/main/java/quickfix/ValidationSettings.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) quickfixengine.org All rights reserved. + * + * This file is part of the QuickFIX FIX Engine + * + * This file may be distributed under the terms of the quickfixengine.org + * license as defined by quickfixengine.org and appearing in the file + * LICENSE included in the packaging of this file. + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING + * THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. + * + * See http://www.quickfixengine.org/LICENSE for licensing information. + * + * Contact ask@quickfixengine.org if any conditions of this licensing + * are not clear to you. + ******************************************************************************/ + +package quickfix; + +public class ValidationSettings { + boolean checkFieldsOutOfOrder = true; + boolean checkFieldsHaveValues = true; + boolean checkUserDefinedFields = true; + boolean checkUnorderedGroupFields = true; + boolean allowUnknownMessageFields = false; + + public ValidationSettings() {} + + public ValidationSettings(ValidationSettings validationSettings) { + this.checkFieldsOutOfOrder = validationSettings.checkFieldsOutOfOrder; + this.checkFieldsHaveValues = validationSettings.checkFieldsHaveValues; + this.checkUserDefinedFields = validationSettings.checkUserDefinedFields; + this.checkUnorderedGroupFields = validationSettings.checkUnorderedGroupFields; + this.allowUnknownMessageFields = validationSettings.allowUnknownMessageFields; + } + + /** + * Controls whether out of order fields are checked. + * + * @param flag true = checked, false = not checked + */ + public void setCheckFieldsOutOfOrder(boolean flag) { + checkFieldsOutOfOrder = flag; + } + + public boolean isCheckFieldsOutOfOrder() { + return checkFieldsOutOfOrder; + } + + public boolean isCheckUnorderedGroupFields() { + return checkUnorderedGroupFields; + } + + public boolean isCheckFieldsHaveValues() { + return checkFieldsHaveValues; + } + + public boolean isCheckUserDefinedFields() { + return checkUserDefinedFields; + } + + public boolean isAllowUnknownMessageFields() { + return allowUnknownMessageFields; + } + + /** + * Controls whether group fields are in the same order + * + * @param flag true = checked, false = not checked + */ + public void setCheckUnorderedGroupFields(boolean flag) { + checkUnorderedGroupFields = flag; + } + + /** + * Controls whether empty field values are checked. + * + * @param flag true = checked, false = not checked + */ + public void setCheckFieldsHaveValues(boolean flag) { + checkFieldsHaveValues = flag; + } + + /** + * Controls whether user defined fields are checked. + * + * @param flag true = checked, false = not checked + */ + public void setCheckUserDefinedFields(boolean flag) { + checkUserDefinedFields = flag; + } + + public void setAllowUnknownMessageFields(boolean allowUnknownFields) { + allowUnknownMessageFields = allowUnknownFields; + } +} diff --git a/quickfixj-base/src/test/java/quickfix/DataDictionaryTest.java b/quickfixj-base/src/test/java/quickfix/DataDictionaryTest.java index 97c87c1359..e8ca0fdc9f 100644 --- a/quickfixj-base/src/test/java/quickfix/DataDictionaryTest.java +++ b/quickfixj-base/src/test/java/quickfix/DataDictionaryTest.java @@ -38,6 +38,13 @@ import quickfix.field.MsgType; import quickfix.field.NoHops; +/** + * NOTE: There are two DataDictionaryTests. + * One in quickfixj-base, one in quickfixj-core, which each test + * some functionality. This test excludes some test cases that cannot + * be tested in this module due to classes that are generated in a + * later step. + */ public class DataDictionaryTest { @Rule @@ -646,20 +653,21 @@ public void testMessageCategory() throws Exception { @Test public void testValidateFieldsOutOfOrderForGroups() throws Exception { final DataDictionary dictionary = new DataDictionary(getDictionary()); - dictionary.setCheckUnorderedGroupFields(false); + ValidationSettings validationSettings = new ValidationSettings(); + validationSettings.setCheckUnorderedGroupFields(false); Message messageWithGroupLevel1 = new Message( "8=FIX.4.4\0019=185\00135=D\00134=25\00149=SENDER\00156=TARGET\00152=20110412-13:43:00\001" + "60=20110412-13:43:00\0011=testAccount\00111=123\00121=3\00138=42\00140=2\00144=42.37\001" + "54=1\00155=QFJ\00159=0\00178=1\00179=allocAccount\001736=currency\001661=1\00110=130\001", - dictionary); - dictionary.validate(messageWithGroupLevel1); + dictionary, validationSettings); + dictionary.validate(messageWithGroupLevel1, validationSettings); Message messageWithGroupLevel2 = new Message( "8=FIX.4.4\0019=185\00135=D\00134=25\00149=SENDER\00156=TARGET\00152=20110412-13:43:00\001" + "60=20110412-13:43:00\0011=testAccount\00111=123\00121=3\00138=42\00140=2\00144=42.37\001" + "54=1\00155=QFJ\00159=0\00178=1\00179=allocAccount\001539=1\001524=1\001538=1\001525=a\00110=145\001", - dictionary); - dictionary.validate(messageWithGroupLevel2); + dictionary, validationSettings); + dictionary.validate(messageWithGroupLevel2, validationSettings); } @Test diff --git a/quickfixj-base/src/test/java/quickfix/MessageTest.java b/quickfixj-base/src/test/java/quickfix/MessageTest.java index 91432d48a5..d9b4c9ebef 100644 --- a/quickfixj-base/src/test/java/quickfix/MessageTest.java +++ b/quickfixj-base/src/test/java/quickfix/MessageTest.java @@ -35,6 +35,13 @@ import quickfix.field.TargetCompID; import quickfix.field.TargetSubID; +/** + * NOTE: There are two MessageTests. + * One in quickfixj-base, one in quickfixj-core, which each test + * some functionality. This test excludes some test cases that cannot + * be tested in this module due to classes that are generated in a + * later step. + */ public class MessageTest { @Rule @@ -84,15 +91,14 @@ public MyMessage() { @Test public void testHeaderFieldWithCustomTransportDictionaryConstructorReadsHeaderField() throws Exception { + ValidationSettings validationSettings = new ValidationSettings(); + validationSettings.setAllowUnknownMessageFields(false); final DataDictionary customSessionDictionary = new DataDictionary("FIXT11_Custom_Test.xml"); - customSessionDictionary.setAllowUnknownMessageFields(false); final DataDictionary standardSessionDictionary = new DataDictionary("FIXT11.xml"); - standardSessionDictionary.setAllowUnknownMessageFields(false); final DataDictionary applicationDictionary = new DataDictionary("FIX50.xml"); - applicationDictionary.setAllowUnknownMessageFields(false); final String sep = "\001"; final StringBuilder sb = new StringBuilder(); @@ -126,7 +132,7 @@ public void testHeaderFieldWithCustomTransportDictionaryConstructorReadsHeaderFi sb.append(sep); final String messageData = sb.toString(); - final Message standardMessage = new Message(messageData, standardSessionDictionary, applicationDictionary, true); + final Message standardMessage = new Message(messageData, standardSessionDictionary, applicationDictionary, validationSettings, true); // Test that field is in body not the header assertTrue(standardMessage.toString().contains("12312=foo")); @@ -135,7 +141,7 @@ public void testHeaderFieldWithCustomTransportDictionaryConstructorReadsHeaderFi assertEquals("foo", standardMessage.getString(12312)); // Test that field is correctly classified in header with customSessionDictionary - final Message customMessage = new Message(messageData, customSessionDictionary, applicationDictionary, true); + final Message customMessage = new Message(messageData, customSessionDictionary, applicationDictionary, validationSettings, true); assertTrue(customMessage.toString().contains("12312=foo")); assertTrue(customMessage.getHeader().isSetField(12312)); assertEquals("foo", customMessage.getHeader().getString(12312)); @@ -483,7 +489,7 @@ public void testParseEmptyString() throws Exception { // without validation try { - new Message(data, DataDictionaryTest.getDictionary(), false); + new Message(data, DataDictionaryTest.getDictionary(), new ValidationSettings(), false); } catch (final InvalidMessage im) { } catch (final Throwable e) { e.printStackTrace(); @@ -562,9 +568,10 @@ public void testMessageGroupCountValidation() throws Exception { "79=AllocACC2\00180=2020.2\001453=2\001448=8\001447=D\001452=4\001448=AAA35354\001447=D\001452=3\00110=079\001"; final Message message = new Message(); final DataDictionary dd = DataDictionaryTest.getDictionary(); - message.fromString(data, dd, true); + final ValidationSettings dds = new ValidationSettings(); + message.fromString(data, dd, dds, true); try { - dd.validate(message); + dd.validate(message, dds); fail("No exception thrown"); } catch (final FieldException e) { final String emsg = e.getMessage(); @@ -587,7 +594,7 @@ public void testMessageWithMissingChecksumField() throws Exception { Message msg = new Message(); try { - msg.fromString(badMessage, DataDictionaryTest.getDictionary(), true); + msg.fromString(badMessage, DataDictionaryTest.getDictionary(), new ValidationSettings(), true); fail(); } catch (final InvalidMessage e) { final String emsg = e.getMessage(); @@ -602,7 +609,7 @@ public void testFalseMessageStructureException() { final DataDictionary dd = DataDictionaryTest.getDictionary(); // duplicated tag 98 // QFJ-65 - new Message("8=FIX.4.4\0019=22\00135=A\00198=0\00198=0\001108=30\00110=223\001", dd, + new Message("8=FIX.4.4\0019=22\00135=A\00198=0\00198=0\001108=30\00110=223\001", dd, new ValidationSettings(), true); // For now, this will not cause an exception if the length and checksum are correct } catch (final Exception e) { @@ -647,7 +654,7 @@ public void testComponentInGroup() { "525=D\001538=51\001524=FCM\001525=D\001538=60 524=U\001525=D\001538=54\001600=217927\001" + "602=BRN FMG0010!\00163=8 608-FXXXXX\001624=1\001637=80.09\001687=1.0\001654=41296073\001" + "9019=1\0019023=1\0019020=20100201\001021=20100228\001", - dd, true); + dd, new ValidationSettings(), true); // For now, this will not cause an exception if the length and checksum are correct } catch (final Exception e) { final String text = e.getMessage(); @@ -661,7 +668,7 @@ public void testFalseMessageStructureException2() { final DataDictionary dd = DataDictionaryTest.getDictionary(); // duplicated raw data length // QFJ-121 - new Message("8=FIX.4.4\0019=22\00135=A\00196=X\001108=30\00110=223\001", dd, true); + new Message("8=FIX.4.4\0019=22\00135=A\00196=X\001108=30\00110=223\001", dd, new ValidationSettings(), true); } catch (final Exception e) { final String text = e.getMessage(); assertTrue("Wrong exception message: " + text, @@ -680,7 +687,7 @@ public void testRepeatingGroupCountWithUnknownFields() throws Exception { DataDictionary dictionary = new DataDictionary(DataDictionaryTest.getDictionary()); Message message = new Message(); - message.fromString(test.replaceAll("\\|", "\001"), dictionary, true); + message.fromString(test.replaceAll("\\|", "\001"), dictionary, new ValidationSettings(), true); Group group = message.getGroup(1, 711); String underlyingSymbol = group.getString(311); assertEquals("780508", underlyingSymbol); @@ -698,7 +705,7 @@ public void testRawString() throws Exception { DataDictionary dictionary = new DataDictionary(DataDictionaryTest.getDictionary()); Message message = new Message(); - message.fromString(test.replaceAll("\\|", "\001"), dictionary, true); + message.fromString(test.replaceAll("\\|", "\001"), dictionary, new ValidationSettings(), true); assertEquals(test, message.toRawString().replaceAll("\001", "\\|")); } @@ -707,7 +714,8 @@ public void testRawString() throws Exception { public void testIfMessageHeaderIsCreatedWithEveryConstructor() throws Exception { final String rawMessage = "8=FIX.4.2\0019=12\00135=A\001108=30\00110=026\001"; final DataDictionary dataDictionary = new DataDictionary(DataDictionaryTest.getDictionary()); - + final ValidationSettings dds = new ValidationSettings(); + final Message emptyConstructor = new Message(); assertNotNull(emptyConstructor.getHeader()); @@ -723,10 +731,10 @@ public void testIfMessageHeaderIsCreatedWithEveryConstructor() throws Exception final Message fifthConstructor = new Message(rawMessage, dataDictionary); assertNotNull(fifthConstructor.getHeader()); - final Message sixthConstructor = new Message(rawMessage, dataDictionary, false); + final Message sixthConstructor = new Message(rawMessage, dataDictionary, dds, false); assertNotNull(sixthConstructor.getHeader()); - final Message seventhConstructor = new Message(rawMessage, dataDictionary, dataDictionary, false); + final Message seventhConstructor = new Message(rawMessage, dataDictionary, dataDictionary, dds, false); assertNotNull(seventhConstructor.getHeader()); } diff --git a/quickfixj-base/src/test/java/quickfix/ValidationSettingsTest.java b/quickfixj-base/src/test/java/quickfix/ValidationSettingsTest.java new file mode 100644 index 0000000000..a46eefff8d --- /dev/null +++ b/quickfixj-base/src/test/java/quickfix/ValidationSettingsTest.java @@ -0,0 +1,26 @@ +package quickfix; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class ValidationSettingsTest { + @Test + public void copyConstructor_retains_settings() { + final ValidationSettings validationSettings = new ValidationSettings(); + + validationSettings.setAllowUnknownMessageFields(true); + validationSettings.setCheckFieldsHaveValues(false); + validationSettings.setCheckFieldsOutOfOrder(false); + validationSettings.setCheckUnorderedGroupFields(false); + validationSettings.setCheckUserDefinedFields(false); + + ValidationSettings validationSettingsCopy = new ValidationSettings(validationSettings); + + assertEquals(validationSettingsCopy.isAllowUnknownMessageFields(), validationSettings.isAllowUnknownMessageFields()); + assertEquals(validationSettingsCopy.isCheckFieldsHaveValues(), validationSettings.isCheckFieldsHaveValues()); + assertEquals(validationSettingsCopy.isCheckFieldsOutOfOrder(), validationSettings.isCheckFieldsOutOfOrder()); + assertEquals(validationSettingsCopy.isCheckUnorderedGroupFields(), validationSettings.isCheckUnorderedGroupFields()); + assertEquals(validationSettingsCopy.isCheckUserDefinedFields(), validationSettings.isCheckUserDefinedFields()); + } +} diff --git a/quickfixj-core/src/main/java/quickfix/DefaultSessionFactory.java b/quickfixj-core/src/main/java/quickfix/DefaultSessionFactory.java index cd83b72206..9da621fe7e 100644 --- a/quickfixj-core/src/main/java/quickfix/DefaultSessionFactory.java +++ b/quickfixj-core/src/main/java/quickfix/DefaultSessionFactory.java @@ -169,6 +169,7 @@ public Session create(SessionID sessionID, SessionSettings settings) throws Conf processPreFixtDataDictionary(sessionID, settings, dataDictionaryProvider); } } + ValidationSettings validationSettings = createValidationSettings(sessionID, settings); int heartbeatInterval = 0; if (connectionType.equals(SessionFactory.INITIATOR_CONNECTION_TYPE)) { @@ -238,7 +239,7 @@ public Session create(SessionID sessionID, SessionSettings settings) throws Conf final List logonTags = getLogonTags(settings, sessionID); final Session session = new Session(application, messageStoreFactory, messageQueueFactory, - sessionID, dataDictionaryProvider, sessionSchedule, logFactory, + sessionID, dataDictionaryProvider, validationSettings, sessionSchedule, logFactory, messageFactory, heartbeatInterval, checkLatency, maxLatency, timestampPrecision, resetOnLogon, resetOnLogout, resetOnDisconnect, refreshOnLogon, checkCompID, redundantResentRequestAllowed, persistMessages, useClosedIntervalForResend, @@ -282,34 +283,38 @@ private void processPreFixtDataDictionary(SessionID sessionID, SessionSettings s private DataDictionary createDataDictionary(SessionID sessionID, SessionSettings settings, String settingsKey, String beginString) throws ConfigError, FieldConvertError { final String path = getDictionaryPath(sessionID, settings, settingsKey, beginString); - final DataDictionary dataDictionary = getDataDictionary(path); + return getDataDictionary(path); + } + + private ValidationSettings createValidationSettings(SessionID sessionID, SessionSettings settings) throws FieldConvertError, ConfigError { + ValidationSettings validationSettings = new ValidationSettings(); if (settings.isSetting(sessionID, Session.SETTING_VALIDATE_FIELDS_OUT_OF_ORDER)) { - dataDictionary.setCheckFieldsOutOfOrder(settings.getBool(sessionID, + validationSettings.setCheckFieldsOutOfOrder(settings.getBool(sessionID, Session.SETTING_VALIDATE_FIELDS_OUT_OF_ORDER)); } if (settings.isSetting(sessionID, Session.SETTING_VALIDATE_FIELDS_HAVE_VALUES)) { - dataDictionary.setCheckFieldsHaveValues(settings.getBool(sessionID, + validationSettings.setCheckFieldsHaveValues(settings.getBool(sessionID, Session.SETTING_VALIDATE_FIELDS_HAVE_VALUES)); } if (settings.isSetting(sessionID, Session.SETTING_VALIDATE_UNORDERED_GROUP_FIELDS)) { - dataDictionary.setCheckUnorderedGroupFields(settings.getBool(sessionID, + validationSettings.setCheckUnorderedGroupFields(settings.getBool(sessionID, Session.SETTING_VALIDATE_UNORDERED_GROUP_FIELDS)); } if (settings.isSetting(sessionID, Session.SETTING_VALIDATE_USER_DEFINED_FIELDS)) { - dataDictionary.setCheckUserDefinedFields(settings.getBool(sessionID, + validationSettings.setCheckUserDefinedFields(settings.getBool(sessionID, Session.SETTING_VALIDATE_USER_DEFINED_FIELDS)); } if (settings.isSetting(sessionID, Session.SETTING_ALLOW_UNKNOWN_MSG_FIELDS)) { - dataDictionary.setAllowUnknownMessageFields(settings.getBool(sessionID, + validationSettings.setAllowUnknownMessageFields(settings.getBool(sessionID, Session.SETTING_ALLOW_UNKNOWN_MSG_FIELDS)); } - return dataDictionary; + return validationSettings; } private void processFixtDataDictionaries(SessionID sessionID, SessionSettings settings, diff --git a/quickfixj-core/src/main/java/quickfix/MessageSessionUtils.java b/quickfixj-core/src/main/java/quickfix/MessageSessionUtils.java index 0bdbafdb8a..6760fb1f9f 100644 --- a/quickfixj-core/src/main/java/quickfix/MessageSessionUtils.java +++ b/quickfixj-core/src/main/java/quickfix/MessageSessionUtils.java @@ -39,6 +39,7 @@ public static Message parse(Session session, String messageString) throws Invali final boolean isLogon = MessageUtils.isLogonMsgType(msgType); final MessageFactory messageFactory = session.getMessageFactory(); final DataDictionaryProvider ddProvider = session.getDataDictionaryProvider(); + final ValidationSettings validationSettings = session.getValidationSettings(); final ApplVerID applVerID; final DataDictionary sessionDataDictionary = ddProvider == null ? null : ddProvider .getSessionDataDictionary(beginString); @@ -68,7 +69,7 @@ public static Message parse(Session session, String messageString) throws Invali final boolean validateChecksum = session.isValidateChecksum(); message = messageFactory.create(beginString, applVerID, msgType); - message.parse(messageString, sessionDataDictionary, payloadDictionary, doValidation, + message.parse(messageString, sessionDataDictionary, payloadDictionary, validationSettings, doValidation, validateChecksum); return message; diff --git a/quickfixj-core/src/main/java/quickfix/Session.java b/quickfixj-core/src/main/java/quickfix/Session.java index 06a88b50d9..20f9e43169 100644 --- a/quickfixj-core/src/main/java/quickfix/Session.java +++ b/quickfixj-core/src/main/java/quickfix/Session.java @@ -413,6 +413,7 @@ public class Session implements Closeable { private long lastSessionLogon = 0; private final DataDictionaryProvider dataDictionaryProvider; + private final ValidationSettings validationSettings; private final boolean checkLatency; private final int maxLatency; private int resendRequestChunkSize = 0; @@ -472,17 +473,20 @@ public class Session implements Closeable { protected static final Logger LOG = LoggerFactory.getLogger(Session.class); Session(Application application, MessageStoreFactory messageStoreFactory, SessionID sessionID, - DataDictionaryProvider dataDictionaryProvider, SessionSchedule sessionSchedule, LogFactory logFactory, - MessageFactory messageFactory, int heartbeatInterval) { - this(application, messageStoreFactory, sessionID, dataDictionaryProvider, sessionSchedule, logFactory, - messageFactory, heartbeatInterval, true, DEFAULT_MAX_LATENCY, UtcTimestampPrecision.MILLIS, false, false, - false, false, true, false, true, false, DEFAULT_TEST_REQUEST_DELAY_MULTIPLIER, null, true, new int[] {5}, - false, false, false, false, true, false, true, false, null, true, DEFAULT_RESEND_RANGE_CHUNK_SIZE, false, - false, false, new ArrayList(), DEFAULT_HEARTBEAT_TIMEOUT_MULTIPLIER, false); + DataDictionaryProvider dataDictionaryProvider, ValidationSettings validationSettings, + SessionSchedule sessionSchedule, + LogFactory logFactory, MessageFactory messageFactory, int heartbeatInterval) { + this(application, messageStoreFactory, sessionID, dataDictionaryProvider, validationSettings, sessionSchedule, + logFactory, messageFactory, heartbeatInterval, true, DEFAULT_MAX_LATENCY, UtcTimestampPrecision.MILLIS, + false, false, false, false, true, false, true, false, + DEFAULT_TEST_REQUEST_DELAY_MULTIPLIER, null, true, new int[] { 5 }, false, false, + false, false, true, false, true, false, null, true, DEFAULT_RESEND_RANGE_CHUNK_SIZE, false, false, false, + new ArrayList(), DEFAULT_HEARTBEAT_TIMEOUT_MULTIPLIER, false); } Session(Application application, MessageStoreFactory messageStoreFactory, SessionID sessionID, - DataDictionaryProvider dataDictionaryProvider, SessionSchedule sessionSchedule, + DataDictionaryProvider dataDictionaryProvider, ValidationSettings validationSettings, + SessionSchedule sessionSchedule, LogFactory logFactory, MessageFactory messageFactory, int heartbeatInterval, boolean checkLatency, int maxLatency, UtcTimestampPrecision timestampPrecision, boolean resetOnLogon, boolean resetOnLogout, boolean resetOnDisconnect, @@ -498,7 +502,7 @@ public class Session implements Closeable { boolean enableNextExpectedMsgSeqNum, boolean enableLastMsgSeqNumProcessed, boolean validateChecksum, List logonTags, double heartBeatTimeoutMultiplier, boolean allowPossDup) { - this(application, messageStoreFactory, new InMemoryMessageQueueFactory(), sessionID, dataDictionaryProvider, sessionSchedule, logFactory, + this(application, messageStoreFactory, new InMemoryMessageQueueFactory(), sessionID, dataDictionaryProvider, validationSettings, sessionSchedule, logFactory, messageFactory, heartbeatInterval, true, DEFAULT_MAX_LATENCY, UtcTimestampPrecision.MILLIS, false, false, false, false, true, false, true, false, DEFAULT_TEST_REQUEST_DELAY_MULTIPLIER, null, true, new int[] {5}, false, false, false, false, true, false, true, false, null, true, DEFAULT_RESEND_RANGE_CHUNK_SIZE, false, @@ -506,7 +510,7 @@ public class Session implements Closeable { } Session(Application application, MessageStoreFactory messageStoreFactory, MessageQueueFactory messageQueueFactory, - SessionID sessionID, DataDictionaryProvider dataDictionaryProvider, SessionSchedule sessionSchedule, + SessionID sessionID, DataDictionaryProvider dataDictionaryProvider, ValidationSettings validationSettings, SessionSchedule sessionSchedule, LogFactory logFactory, MessageFactory messageFactory, int heartbeatInterval, boolean checkLatency, int maxLatency, UtcTimestampPrecision timestampPrecision, boolean resetOnLogon, boolean resetOnLogout, boolean resetOnDisconnect, @@ -533,6 +537,7 @@ public class Session implements Closeable { this.timestampPrecision = timestampPrecision; this.refreshOnLogon = refreshOnLogon; this.dataDictionaryProvider = dataDictionaryProvider; + this.validationSettings = validationSettings; this.messageFactory = messageFactory; this.checkCompID = checkCompID; this.redundantResentRequestsAllowed = redundantResentRequestsAllowed; @@ -1041,7 +1046,7 @@ private void next(Message message, boolean isProcessingQueuedMessages) throws Fi } } - if (validateIncomingMessage && dataDictionaryProvider != null) { + if (validateIncomingMessage && dataDictionaryProvider != null && validationSettings != null) { final DataDictionary sessionDataDictionary = dataDictionaryProvider .getSessionDataDictionary(beginString); @@ -1056,7 +1061,7 @@ private void next(Message message, boolean isProcessingQueuedMessages) throws Fi // related to QFJ-367 : just warn invalid incoming field/tags try { DataDictionary.validate(message, sessionDataDictionary, - applicationDataDictionary); + applicationDataDictionary, validationSettings); } catch (final IncorrectTagValue e) { if (rejectInvalidMessage) { throw e; @@ -2813,6 +2818,10 @@ public DataDictionaryProvider getDataDictionaryProvider() { return dataDictionaryProvider; } + public ValidationSettings getValidationSettings() { + return validationSettings; + } + public SessionID getSessionID() { return sessionID; } diff --git a/quickfixj-core/src/test/java/quickfix/DataDictionaryTest.java b/quickfixj-core/src/test/java/quickfix/DataDictionaryTest.java index add2d6fe1a..6233b22781 100644 --- a/quickfixj-core/src/test/java/quickfix/DataDictionaryTest.java +++ b/quickfixj-core/src/test/java/quickfix/DataDictionaryTest.java @@ -32,6 +32,7 @@ import java.util.concurrent.Callable; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import static org.junit.Assert.assertNotNull; import org.junit.Rule; import org.junit.Test; @@ -73,11 +74,61 @@ import quickfix.fix44.QuoteRequest; import quickfix.test.util.ExpectedTestFailure; +/** + * NOTE: There are two DataDictionaryTests. One in quickfixj-base, one in + * quickfixj-core, which each test some functionality. This test covers some + * test cases that cannot be tested in the quickfixj-base module due to usage of + * message classes that are generated later in the compile process. + */ public class DataDictionaryTest { @Rule public final ExpectedException expectedException = ExpectedException.none(); + @Test + public void testDictionary() throws Exception { + DataDictionary dd = getDictionary(); + + assertEquals("wrong field name", "Currency", dd.getFieldName(15)); + assertEquals("wrong field tag", 15, dd.getFieldTag("Currency")); + assertEquals("wrong value description", "BUY", dd.getValueName(4, "B")); + assertEquals("wrong value type", FieldType.STRING, dd.getFieldType(1)); + assertEquals("wrong version", FixVersions.BEGINSTRING_FIX44, dd.getVersion()); + assertEquals("incorrectly validates values", false, dd.isFieldValue(15, "10")); + assertEquals("incorrectly validates valid value", true, dd.isFieldValue(4, "B")); + assertEquals("incorrectly validates invalid value", false, dd.isFieldValue(4, "C")); + assertEquals("incorrectly validates multiple values", true, dd.isFieldValue(277, "A K")); + assertFalse("unexpected field values existence", dd.hasFieldValue(1)); + assertTrue("unexpected field values nonexistence", dd.hasFieldValue(4)); + assertFalse("unexpected field existence", dd.isField(9999)); + assertTrue("unexpected field nonexistence", dd.isField(4)); + assertTrue("unexpected field value existence", !dd.isFieldValue(4, "C")); + assertTrue("unexpected field value nonexistence", dd.isFieldValue(4, "B")); + assertTrue("wrong group info", dd.isGroup("A", 384)); + assertFalse("wrong group info", dd.isGroup("A", 1)); + assertNotNull("wrong group info", dd.getGroup("6", 232)); + assertTrue("incorrect header field", dd.isHeaderField(8)); + assertFalse("incorrect header field", dd.isHeaderField(1)); + assertTrue("incorrect trailer field", dd.isTrailerField(89)); + assertFalse("incorrect trailer field", dd.isTrailerField(1)); + assertTrue("incorrect message field", dd.isMsgField("A", 98)); + assertFalse("incorrect message field", dd.isMsgField("A", 1)); + // component field + assertTrue("incorrect message field", dd.isMsgField("6", 235)); + // group->component field + //assertTrue("incorrect message field", dd.isMsgField("6", 311)); + assertTrue("incorrect message type", dd.isMsgType("A")); + assertFalse("incorrect message type", dd.isMsgType("%")); + assertTrue("incorrect field requirement", dd.isRequiredField("A", 98)); + assertFalse("incorrect field requirement", dd.isRequiredField("A", 95)); + assertEquals("incorrect field name", "Account", dd.getFieldName(1)); + assertEquals("incorrect msg type", "0", dd.getMsgType("Heartbeat")); + assertEquals("incorrect msg type", "B", dd.getMsgType("News")); + assertFalse(dd.isMsgField("UNKNOWN_TYPE", 1)); + } + + + @Test public void testMessageValidateBodyOnly() throws Exception { final quickfix.fix44.NewOrderSingle newSingle = new quickfix.fix44.NewOrderSingle( @@ -95,11 +146,11 @@ public void testMessageValidateBodyOnly() throws Exception { new ExpectedTestFailure(FieldException.class, "field=") { @Override protected void execute() throws Throwable { - dd.validate(newSingle); + dd.validate(newSingle, new ValidationSettings()); } }.run(); - dd.validate(newSingle, true); + dd.validate(newSingle, true, new ValidationSettings()); } @Test @@ -118,13 +169,13 @@ public void testMessageDataDictionaryMismatch() throws Exception { "Message version 'FIX.4.3' does not match the data dictionary version 'FIX.4.4'") { @Override protected void execute() throws Throwable { - dd.validate(newSingle); + dd.validate(newSingle, new ValidationSettings()); } }.run(); // TODO: This is unexpected for pre-FIX 5.0 messages: // If bodyOnly is true, the correct data dictionary is not checked. - dd.validate(newSingle, true); + dd.validate(newSingle, true, new ValidationSettings()); } @Test @@ -150,16 +201,17 @@ public void testAllowUnknownFields() throws Exception { newSingle.setField(new LastMkt("FOO")); final DataDictionary dictionary = new DataDictionary(getDictionary()); + final ValidationSettings validationSettings = new ValidationSettings(); new ExpectedTestFailure(FieldException.class, "field=") { @Override protected void execute() throws Throwable { - dictionary.validate(newSingle); + dictionary.validate(newSingle, validationSettings); } }.run(); - dictionary.setAllowUnknownMessageFields(true); - dictionary.validate(newSingle); + validationSettings.setAllowUnknownMessageFields(true); + dictionary.validate(newSingle, validationSettings); } // QFJ-535 @@ -167,7 +219,8 @@ protected void execute() throws Throwable { public void testNewOrderSingleWithCorrectTag50() throws Exception { final DataDictionary dataDictionary = new DataDictionary(getDictionary()); - dataDictionary.setCheckFieldsOutOfOrder(true); + final ValidationSettings validationSettings = new ValidationSettings(); + validationSettings.setCheckFieldsOutOfOrder(true); String correctFixMessage = "8=FIX.4.4\0019=218\00135=D\00149=cust\00150=trader\001" + "56=FixGateway\00134=449\00152=20110420-09:17:40\00111=clordid\00154=1\00138=50\001" + @@ -177,28 +230,28 @@ public void testNewOrderSingleWithCorrectTag50() throws Exception { // in any case, it must be validated as the message is correct //doValidation and checkFieldsOutOfOrder final NewOrderSingle nos1 = new NewOrderSingle(); - nos1.fromString(correctFixMessage, dataDictionary, true); - dataDictionary.validate(nos1); + nos1.fromString(correctFixMessage, dataDictionary, validationSettings, true); + dataDictionary.validate(nos1, validationSettings); assertTrue(nos1.getHeader().isSetField(new SenderSubID())); //doNotValidation and checkFieldsOutOfOrder final NewOrderSingle nos2 = new NewOrderSingle(); - nos2.fromString(correctFixMessage, dataDictionary, false); - dataDictionary.validate(nos2); + nos2.fromString(correctFixMessage, dataDictionary, validationSettings, false); + dataDictionary.validate(nos2, validationSettings); assertTrue(nos2.getHeader().isSetField(new SenderSubID())); - dataDictionary.setCheckFieldsOutOfOrder(false); + validationSettings.setCheckFieldsOutOfOrder(false); //doValidation and no checkFieldsOutOfOrder final NewOrderSingle nos3 = new NewOrderSingle(); - nos3.fromString(correctFixMessage, dataDictionary, true); - dataDictionary.validate(nos3); + nos3.fromString(correctFixMessage, dataDictionary, validationSettings, true); + dataDictionary.validate(nos3, validationSettings); assertTrue(nos3.getHeader().isSetField(new SenderSubID())); //doNotValidation and no checkFieldsOutOfOrder final NewOrderSingle nos4 = new NewOrderSingle(); - nos4.fromString(correctFixMessage, dataDictionary, false); - dataDictionary.validate(nos4); + nos4.fromString(correctFixMessage, dataDictionary, validationSettings, false); + dataDictionary.validate(nos4, validationSettings); assertTrue(nos4.getHeader().isSetField(new SenderSubID())); } @@ -206,7 +259,8 @@ public void testNewOrderSingleWithCorrectTag50() throws Exception { public void testNewOrderSingleWithMisplacedTag50() throws Exception { final DataDictionary dataDictionary = new DataDictionary(getDictionary()); - dataDictionary.setCheckFieldsOutOfOrder(true); + final ValidationSettings validationSettings = new ValidationSettings(); + validationSettings.setCheckFieldsOutOfOrder(true); String incorrectFixMessage = "8=FIX.4.4\0019=218\00135=D\00149=cust\00156=FixGateway\001" + "34=449\00152=20110420-09:17:40\00111=clordid\00154=1\00138=50\00159=6\00140=2\001" + @@ -216,29 +270,29 @@ public void testNewOrderSingleWithMisplacedTag50() throws Exception { //doValidation and checkFieldsOutOfOrder -> should fail final NewOrderSingle nos1 = new NewOrderSingle(); try { - nos1.fromString(incorrectFixMessage, dataDictionary, true); + nos1.fromString(incorrectFixMessage, dataDictionary, validationSettings, true); } catch (FieldException fe) { // expected exception } //doNotValidation and checkFieldsOutOfOrder -> should NOT fail final NewOrderSingle nos2 = new NewOrderSingle(); - nos2.fromString(incorrectFixMessage, dataDictionary, false); - dataDictionary.validate(nos2); + nos2.fromString(incorrectFixMessage, dataDictionary, validationSettings, false); + dataDictionary.validate(nos2, validationSettings); assertTrue(nos2.getHeader().isSetField(new SenderSubID())); - dataDictionary.setCheckFieldsOutOfOrder(false); + validationSettings.setCheckFieldsOutOfOrder(false); //doValidation and no checkFieldsOutOfOrder -> should NOT fail final NewOrderSingle nos3 = new NewOrderSingle(); - nos3.fromString(incorrectFixMessage, dataDictionary, true); - dataDictionary.validate(nos3); + nos3.fromString(incorrectFixMessage, dataDictionary, validationSettings, true); + dataDictionary.validate(nos3, validationSettings); assertTrue(nos3.getHeader().isSetField(new SenderSubID())); //doNotValidation and no checkFieldsOutOfOrder -> should NOT fail final NewOrderSingle nos4 = new NewOrderSingle(); - nos4.fromString(incorrectFixMessage, dataDictionary, false); - dataDictionary.validate(nos4); + nos4.fromString(incorrectFixMessage, dataDictionary, validationSettings, false); + dataDictionary.validate(nos4, validationSettings); assertTrue(nos4.getHeader().isSetField(new SenderSubID())); } @@ -246,32 +300,12 @@ public void testNewOrderSingleWithMisplacedTag50() throws Exception { public void testCopy() throws Exception { final DataDictionary dataDictionary = new DataDictionary(getDictionary()); - dataDictionary.setAllowUnknownMessageFields(true); - dataDictionary.setCheckFieldsHaveValues(false); - dataDictionary.setCheckFieldsOutOfOrder(false); - dataDictionary.setCheckUnorderedGroupFields(false); - dataDictionary.setCheckUserDefinedFields(false); - DataDictionary ddCopy = new DataDictionary(dataDictionary); - assertEquals(ddCopy.isAllowUnknownMessageFields(),dataDictionary.isAllowUnknownMessageFields()); - assertEquals(ddCopy.isCheckFieldsHaveValues(),dataDictionary.isCheckFieldsHaveValues()); - assertEquals(ddCopy.isCheckFieldsOutOfOrder(),dataDictionary.isCheckFieldsOutOfOrder()); - assertEquals(ddCopy.isCheckUnorderedGroupFields(),dataDictionary.isCheckUnorderedGroupFields()); - assertEquals(ddCopy.isCheckUserDefinedFields(),dataDictionary.isCheckUserDefinedFields()); assertArrayEquals(getDictionary().getOrderedFields(),ddCopy.getOrderedFields()); assertArrayEquals(getDictionary().getOrderedFields(),dataDictionary.getOrderedFields()); DataDictionary.GroupInfo groupFromDDCopy = ddCopy.getGroup(NewOrderSingle.MSGTYPE, NoPartyIDs.FIELD); - assertTrue(groupFromDDCopy.getDataDictionary().isAllowUnknownMessageFields()); - // set to false on ORIGINAL DD - dataDictionary.setAllowUnknownMessageFields(false); - assertFalse(dataDictionary.isAllowUnknownMessageFields()); - assertFalse(dataDictionary.getGroup(NewOrderSingle.MSGTYPE, NoPartyIDs.FIELD).getDataDictionary().isAllowUnknownMessageFields()); - // should be still true on COPIED DD and its group - assertTrue(ddCopy.isAllowUnknownMessageFields()); - groupFromDDCopy = ddCopy.getGroup(NewOrderSingle.MSGTYPE, NoPartyIDs.FIELD); - assertTrue(groupFromDDCopy.getDataDictionary().isAllowUnknownMessageFields()); DataDictionary originalGroupDictionary = getDictionary().getGroup(NewOrderSingle.MSGTYPE, NoPartyIDs.FIELD).getDataDictionary(); DataDictionary groupDictionary = dataDictionary.getGroup(NewOrderSingle.MSGTYPE, NoPartyIDs.FIELD).getDataDictionary(); @@ -317,12 +351,13 @@ public void testNonUDFDefinedInFieldsSectionDontAllowUMFDontCheckUDF() throws Ex quoteRequest.setDecimal(AvgPx.FIELD, new BigDecimal(1.2345)); DataDictionary dataDictionary = new DataDictionary("FIX44_Custom_Test.xml"); - dataDictionary.setAllowUnknownMessageFields(false); - dataDictionary.setCheckUserDefinedFields(false); + ValidationSettings validationSettings = new ValidationSettings(); + validationSettings.setAllowUnknownMessageFields(false); + validationSettings.setCheckUserDefinedFields(false); expectedException.expect(FieldException.class); expectedException.expectMessage("Tag not defined for this message type, field=6"); - dataDictionary.validate(quoteRequest, true); + dataDictionary.validate(quoteRequest, true, validationSettings); } @Test @@ -331,12 +366,13 @@ public void testNonUDFDefinedInFieldsSectionDontAllowUMFCheckUDF() throws Except quoteRequest.setDecimal(AvgPx.FIELD, new BigDecimal(1.2345)); DataDictionary dataDictionary = new DataDictionary("FIX44_Custom_Test.xml"); - dataDictionary.setAllowUnknownMessageFields(false); - dataDictionary.setCheckUserDefinedFields(true); + ValidationSettings validationSettings = new ValidationSettings(); + validationSettings.setAllowUnknownMessageFields(false); + validationSettings.setCheckUserDefinedFields(true); expectedException.expect(FieldException.class); expectedException.expectMessage("Tag not defined for this message type, field=6"); - dataDictionary.validate(quoteRequest, true); + dataDictionary.validate(quoteRequest, true, validationSettings); } @Test @@ -345,10 +381,11 @@ public void testNonUDFDefinedInFieldsSectionAllowUMFDontCheckUDF() throws Except quoteRequest.setDecimal(AvgPx.FIELD, new BigDecimal(1.2345)); DataDictionary dataDictionary = new DataDictionary("FIX44_Custom_Test.xml"); - dataDictionary.setAllowUnknownMessageFields(true); - dataDictionary.setCheckUserDefinedFields(false); + ValidationSettings validationSettings = new ValidationSettings(); + validationSettings.setAllowUnknownMessageFields(true); + validationSettings.setCheckUserDefinedFields(false); - dataDictionary.validate(quoteRequest, true); + dataDictionary.validate(quoteRequest, true, validationSettings); } @Test @@ -357,10 +394,11 @@ public void testNonUDFDefinedInFieldsSectionAllowUMFCheckUDF() throws Exception quoteRequest.setDecimal(AvgPx.FIELD, new BigDecimal(1.2345)); DataDictionary dataDictionary = new DataDictionary("FIX44_Custom_Test.xml"); - dataDictionary.setAllowUnknownMessageFields(true); - dataDictionary.setCheckUserDefinedFields(true); + ValidationSettings validationSettings = new ValidationSettings(); + validationSettings.setAllowUnknownMessageFields(true); + validationSettings.setCheckUserDefinedFields(true); - dataDictionary.validate(quoteRequest, true); + dataDictionary.validate(quoteRequest, true, validationSettings); } /** @@ -381,10 +419,11 @@ public void testUDFDefinedInFieldsSectionDontAllowUMFDontCheckUDF() throws Excep quoteRequest.setInt(5000, 555); DataDictionary dataDictionary = new DataDictionary("FIX44_Custom_Test.xml"); - dataDictionary.setAllowUnknownMessageFields(false); - dataDictionary.setCheckUserDefinedFields(false); + ValidationSettings validationSettings = new ValidationSettings(); + validationSettings.setAllowUnknownMessageFields(false); + validationSettings.setCheckUserDefinedFields(false); - dataDictionary.validate(quoteRequest, true); + dataDictionary.validate(quoteRequest, true, validationSettings); } @Test @@ -393,12 +432,13 @@ public void testUDFDefinedInFieldsSectionDontAllowUMFCheckUDF() throws Exception quoteRequest.setInt(5000, 555); DataDictionary dataDictionary = new DataDictionary("FIX44_Custom_Test.xml"); - dataDictionary.setAllowUnknownMessageFields(false); - dataDictionary.setCheckUserDefinedFields(true); + ValidationSettings validationSettings = new ValidationSettings(); + validationSettings.setAllowUnknownMessageFields(false); + validationSettings.setCheckUserDefinedFields(true); expectedException.expect(FieldException.class); expectedException.expectMessage("Tag not defined for this message type, field=5000"); - dataDictionary.validate(quoteRequest, true); + dataDictionary.validate(quoteRequest, true, validationSettings); } @Test @@ -407,10 +447,11 @@ public void testUDFDefinedInFieldsSectionAllowUMFDontCheckUDF() throws Exception quoteRequest.setInt(5000, 555); DataDictionary dataDictionary = new DataDictionary("FIX44_Custom_Test.xml"); - dataDictionary.setAllowUnknownMessageFields(true); - dataDictionary.setCheckUserDefinedFields(false); + ValidationSettings validationSettings = new ValidationSettings(); + validationSettings.setAllowUnknownMessageFields(true); + validationSettings.setCheckUserDefinedFields(false); - dataDictionary.validate(quoteRequest, true); + dataDictionary.validate(quoteRequest, true, validationSettings); } @Test @@ -419,12 +460,13 @@ public void testUDFDefinedInFieldsSectionAllowUMFCheckUDF() throws Exception { quoteRequest.setInt(5000, 555); DataDictionary dataDictionary = new DataDictionary("FIX44_Custom_Test.xml"); - dataDictionary.setAllowUnknownMessageFields(true); - dataDictionary.setCheckUserDefinedFields(true); + ValidationSettings validationSettings = new ValidationSettings(); + validationSettings.setAllowUnknownMessageFields(true); + validationSettings.setCheckUserDefinedFields(true); expectedException.expect(FieldException.class); expectedException.expectMessage("Tag not defined for this message type, field=5000"); - dataDictionary.validate(quoteRequest, true); + dataDictionary.validate(quoteRequest, true, validationSettings); } /** @@ -445,12 +487,13 @@ public void testNonUDFNotDefinedInFieldsSectionDontAllowUMFDontCheckUDF() throws quoteRequest.setInt(1000, 111); DataDictionary dataDictionary = new DataDictionary("FIX44_Custom_Test.xml"); - dataDictionary.setAllowUnknownMessageFields(false); - dataDictionary.setCheckUserDefinedFields(false); + ValidationSettings validationSettings = new ValidationSettings(); + validationSettings.setAllowUnknownMessageFields(false); + validationSettings.setCheckUserDefinedFields(false); expectedException.expect(FieldException.class); expectedException.expectMessage("Invalid tag number, field=1000"); - dataDictionary.validate(quoteRequest, true); + dataDictionary.validate(quoteRequest, true, validationSettings); } @Test @@ -459,12 +502,13 @@ public void testNonUDFNotDefinedInFieldsSectionDontAllowUMFCheckUDF() throws Exc quoteRequest.setInt(1000, 111); DataDictionary dataDictionary = new DataDictionary("FIX44_Custom_Test.xml"); - dataDictionary.setAllowUnknownMessageFields(false); - dataDictionary.setCheckUserDefinedFields(true); + ValidationSettings validationSettings = new ValidationSettings(); + validationSettings.setAllowUnknownMessageFields(false); + validationSettings.setCheckUserDefinedFields(true); expectedException.expect(FieldException.class); expectedException.expectMessage("Invalid tag number, field=1000"); - dataDictionary.validate(quoteRequest, true); + dataDictionary.validate(quoteRequest, true, validationSettings); } @Test @@ -473,10 +517,11 @@ public void testNonUDFNotDefinedInFieldsSectionAllowUMFDontCheckUDF() throws Exc quoteRequest.setInt(1000, 111); DataDictionary dataDictionary = new DataDictionary("FIX44_Custom_Test.xml"); - dataDictionary.setAllowUnknownMessageFields(true); - dataDictionary.setCheckUserDefinedFields(false); + ValidationSettings validationSettings = new ValidationSettings(); + validationSettings.setAllowUnknownMessageFields(true); + validationSettings.setCheckUserDefinedFields(false); - dataDictionary.validate(quoteRequest, true); + dataDictionary.validate(quoteRequest, true, validationSettings); } @Test @@ -485,10 +530,11 @@ public void testNonUDFNotDefinedInFieldsSectionAllowUMFCheckUDF() throws Excepti quoteRequest.setInt(1000, 111); DataDictionary dataDictionary = new DataDictionary("FIX44_Custom_Test.xml"); - dataDictionary.setAllowUnknownMessageFields(true); - dataDictionary.setCheckUserDefinedFields(true); + ValidationSettings validationSettings = new ValidationSettings(); + validationSettings.setAllowUnknownMessageFields(true); + validationSettings.setCheckUserDefinedFields(true); - dataDictionary.validate(quoteRequest, true); + dataDictionary.validate(quoteRequest, true, validationSettings); } /** @@ -509,10 +555,11 @@ public void testUDFNotDefinedInFieldsSectionDontAllowUMFDontCheckUDF() throws Ex quoteRequest.setInt(6000, 666); DataDictionary dataDictionary = new DataDictionary("FIX44_Custom_Test.xml"); - dataDictionary.setAllowUnknownMessageFields(false); - dataDictionary.setCheckUserDefinedFields(false); + ValidationSettings validationSettings = new ValidationSettings(); + validationSettings.setAllowUnknownMessageFields(false); + validationSettings.setCheckUserDefinedFields(false); - dataDictionary.validate(quoteRequest, true); + dataDictionary.validate(quoteRequest, true, validationSettings); } @Test @@ -521,12 +568,13 @@ public void testUDFNotDefinedInFieldsSectionDontAllowUMFCheckUDF() throws Except quoteRequest.setInt(6000, 666); DataDictionary dataDictionary = new DataDictionary("FIX44_Custom_Test.xml"); - dataDictionary.setAllowUnknownMessageFields(false); - dataDictionary.setCheckUserDefinedFields(true); + ValidationSettings validationSettings = new ValidationSettings(); + validationSettings.setAllowUnknownMessageFields(false); + validationSettings.setCheckUserDefinedFields(true); expectedException.expect(FieldException.class); expectedException.expectMessage("Invalid tag number, field=6000"); - dataDictionary.validate(quoteRequest, true); + dataDictionary.validate(quoteRequest, true, validationSettings); } @Test @@ -535,10 +583,11 @@ public void testUDFNotDefinedInFieldsSectionAllowUMFDontCheckUDF() throws Except quoteRequest.setInt(6000, 666); DataDictionary dataDictionary = new DataDictionary("FIX44_Custom_Test.xml"); - dataDictionary.setAllowUnknownMessageFields(true); - dataDictionary.setCheckUserDefinedFields(false); + ValidationSettings validationSettings = new ValidationSettings(); + validationSettings.setAllowUnknownMessageFields(true); + validationSettings.setCheckUserDefinedFields(false); - dataDictionary.validate(quoteRequest, true); + dataDictionary.validate(quoteRequest, true, validationSettings); } @Test @@ -547,12 +596,13 @@ public void testUDFNotDefinedInFieldsSectionAllowUMFCheckUDF() throws Exception quoteRequest.setInt(6000, 666); DataDictionary dataDictionary = new DataDictionary("FIX44_Custom_Test.xml"); - dataDictionary.setAllowUnknownMessageFields(true); - dataDictionary.setCheckUserDefinedFields(true); + ValidationSettings validationSettings = new ValidationSettings(); + validationSettings.setAllowUnknownMessageFields(true); + validationSettings.setCheckUserDefinedFields(true); expectedException.expect(FieldException.class); expectedException.expectMessage("Invalid tag number, field=6000"); - dataDictionary.validate(quoteRequest, true); + dataDictionary.validate(quoteRequest, true, validationSettings); } private Message createQuoteRequest() { @@ -581,12 +631,13 @@ public void testGroupWithReqdComponentWithReqdFieldValidation() throws Exception final Message quoteRequest = createQuoteRequest(); quoteRequest.getGroup(1, NoRelatedSym.FIELD).removeField(Symbol.FIELD); final DataDictionary dictionary = getDictionary(); + final ValidationSettings validationSettings = new ValidationSettings(); expectedException.expect(FieldException.class); expectedException.expect(hasProperty("sessionRejectReason", is(SessionRejectReason.REQUIRED_TAG_MISSING))); expectedException.expect(hasProperty("field", is(Symbol.FIELD))); - dictionary.validate(quoteRequest, true); + dictionary.validate(quoteRequest, true, validationSettings); } @Test @@ -608,7 +659,8 @@ public void testRequiredFieldInsideComponentWithinRepeatingGroup() throws Except @Test public void testAllowingBlankValuesDisablesFieldValidation() throws Exception { final DataDictionary dictionary = getDictionary(); - dictionary.setCheckFieldsHaveValues(false); + ValidationSettings validationSettings = new ValidationSettings(); + validationSettings.setCheckFieldsHaveValues(false); final quickfix.fix44.NewOrderSingle newSingle = new quickfix.fix44.NewOrderSingle( new ClOrdID("123"), new Side(Side.BUY), new TransactTime(), new OrdType(OrdType.LIMIT) ); @@ -620,7 +672,7 @@ public void testAllowingBlankValuesDisablesFieldValidation() throws Exception { newSingle.setField(new TimeInForce(TimeInForce.DAY)); newSingle.setField(new Account("testAccount")); newSingle.setField(new StringField(EffectiveTime.FIELD)); - dictionary.validate(newSingle, true); + dictionary.validate(newSingle, true, validationSettings); } @@ -645,7 +697,7 @@ public void testConcurrentValidationFailure() throws Exception { List resultList = new ArrayList<>(); for (int j = 0; j < noOfThreads; j++) { final Callable messageParser = (Callable) () -> { - Message msg = MessageUtils.parse(messageFactory, dd, msgString); + Message msg = MessageUtils.parse(messageFactory, dd, new ValidationSettings(), msgString); Group partyGroup = msg.getGroups(quickfix.field.NoPartyIDs.FIELD).get(0); char partyIdSource = partyGroup.getChar(PartyIDSource.FIELD); assertEquals(PartyIDSource.PROPRIETARY, partyIdSource); diff --git a/quickfixj-core/src/test/java/quickfix/DefaultSessionFactoryTest.java b/quickfixj-core/src/test/java/quickfix/DefaultSessionFactoryTest.java index 3b8069061f..57eb7bea7b 100644 --- a/quickfixj-core/src/test/java/quickfix/DefaultSessionFactoryTest.java +++ b/quickfixj-core/src/test/java/quickfix/DefaultSessionFactoryTest.java @@ -36,6 +36,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.fail; public class DefaultSessionFactoryTest { @@ -97,6 +98,8 @@ public void testFixtDataDictionaryConfiguration() throws Exception { settings.setString(sessionID, Session.SETTING_DEFAULT_APPL_VER_ID, "FIX.4.2"); settings.setString(sessionID, Session.SETTING_APP_DATA_DICTIONARY, "FIX42.xml"); settings.setString(sessionID, Session.SETTING_APP_DATA_DICTIONARY + "." + FixVersions.BEGINSTRING_FIX40, "FIX40.xml"); + settings.setString(sessionID, Session.SETTING_ALLOW_UNKNOWN_MSG_FIELDS, "Y"); + settings.setString(sessionID, Session.SETTING_VALIDATE_UNORDERED_GROUP_FIELDS, "N"); try (Session session = factory.create(sessionID, settings)) { @@ -108,6 +111,37 @@ public void testFixtDataDictionaryConfiguration() throws Exception { is(notNullValue())); assertThat(provider.getApplicationDataDictionary(new ApplVerID(ApplVerID.FIX40)), is(notNullValue())); + assertTrue(session.getValidationSettings().isAllowUnknownMessageFields()); + assertFalse(session.getValidationSettings().isCheckUnorderedGroupFields()); + } + } + + /** + * Tests that validation settings are applied when no explicit AppDataDictionary is defined. + * QFJ-981 + */ + @Test + public void testFixtDataDictionaryConfigurationWithDefaultAppDataDictionary() throws Exception { + SessionID sessionID = new SessionID(FixVersions.BEGINSTRING_FIXT11, "SENDER", "TARGET"); + setUpDefaultSettings(sessionID); + settings.setBool(sessionID, Session.SETTING_USE_DATA_DICTIONARY, true); + settings.setString(sessionID, Session.SETTING_TRANSPORT_DATA_DICTIONARY, "FIXT11.xml"); + settings.setString(sessionID, Session.SETTING_DEFAULT_APPL_VER_ID, "FIX.4.2"); + settings.setString(sessionID, Session.SETTING_ALLOW_UNKNOWN_MSG_FIELDS, "Y"); + settings.setString(sessionID, Session.SETTING_VALIDATE_UNORDERED_GROUP_FIELDS, "N"); + + try (Session session = factory.create(sessionID, settings)) { + + DataDictionaryProvider provider = session.getDataDictionaryProvider(); + assertThat(provider.getSessionDataDictionary(sessionID.getBeginString()), + is(notNullValue())); + + assertThat(provider.getApplicationDataDictionary(new ApplVerID(ApplVerID.FIX42)), + is(notNullValue())); + assertThat(provider.getApplicationDataDictionary(new ApplVerID(ApplVerID.FIX40)), + is(notNullValue())); + assertTrue(session.getValidationSettings().isAllowUnknownMessageFields()); + assertFalse(session.getValidationSettings().isCheckUnorderedGroupFields()); } } diff --git a/quickfixj-core/src/test/java/quickfix/FieldTest.java b/quickfixj-core/src/test/java/quickfix/FieldTest.java index 879ade68fb..35b9c54709 100644 --- a/quickfixj-core/src/test/java/quickfix/FieldTest.java +++ b/quickfixj-core/src/test/java/quickfix/FieldTest.java @@ -283,7 +283,7 @@ public void testMultipleStringValue() throws Exception { md.addGroup(value); DataDictionary dd = new DataDictionary("FIX50.xml"); - dd.validate(md); + dd.validate(md, new ValidationSettings()); } @Test @@ -298,7 +298,7 @@ public void testMultipleCharValue() throws Exception { nos.set(new TransactTime(LocalDateTime.of(2020, 3, 10, 12, 23, 44))); DataDictionary dd = new DataDictionary("FIX50.xml"); - dd.validate(nos); + dd.validate(nos, new ValidationSettings()); } private void assertEqualsAndHash(Field field1, Field field2) { diff --git a/quickfixj-core/src/test/java/quickfix/FileLogTest.java b/quickfixj-core/src/test/java/quickfix/FileLogTest.java index 15dd38d6c5..db0a0e1a91 100644 --- a/quickfixj-core/src/test/java/quickfix/FileLogTest.java +++ b/quickfixj-core/src/test/java/quickfix/FileLogTest.java @@ -209,7 +209,7 @@ public void testLogErrorWhenFilesystemRemoved() throws IOException { FileLogFactory factory = new FileLogFactory(settings); try (Session session = new Session(new UnitTestApplication(), new MemoryStoreFactory(), - sessionID, new DefaultDataDictionaryProvider(), null, factory, + sessionID, new DefaultDataDictionaryProvider(), new ValidationSettings(), null, factory, new DefaultMessageFactory(), 0)) { Session.registerSession(session); diff --git a/quickfixj-core/src/test/java/quickfix/JdbcLogTest.java b/quickfixj-core/src/test/java/quickfix/JdbcLogTest.java index 7436d7ca74..c9be355493 100644 --- a/quickfixj-core/src/test/java/quickfix/JdbcLogTest.java +++ b/quickfixj-core/src/test/java/quickfix/JdbcLogTest.java @@ -101,7 +101,7 @@ public void testHandlesRecursivelyFailingException() throws Exception { // need to register the session since we are going to log errors through LogUtil Session.registerSession(new Session(new UnitTestApplication(), new MemoryStoreFactory(), - sessionID, new DefaultDataDictionaryProvider(), null, logFactory, + sessionID, new DefaultDataDictionaryProvider(), new ValidationSettings(), null, logFactory, new DefaultMessageFactory(), 0)); // remove the messages and events tables diff --git a/quickfixj-core/src/test/java/quickfix/LogUtilTest.java b/quickfixj-core/src/test/java/quickfix/LogUtilTest.java index f6b5af42e4..aee0925266 100644 --- a/quickfixj-core/src/test/java/quickfix/LogUtilTest.java +++ b/quickfixj-core/src/test/java/quickfix/LogUtilTest.java @@ -69,7 +69,7 @@ public Calendar getCreationTimeCalendar() throws IOException { // ignore return null; } - }, sessionID, null, schedule, mockLogFactory, null, 0); + }, sessionID, null, null, schedule, mockLogFactory, null, 0); try { session.close(); } catch (IOException e) { diff --git a/quickfixj-core/src/test/java/quickfix/MessageTest.java b/quickfixj-core/src/test/java/quickfix/MessageTest.java index 194f4d39bb..9ae2ad0f52 100644 --- a/quickfixj-core/src/test/java/quickfix/MessageTest.java +++ b/quickfixj-core/src/test/java/quickfix/MessageTest.java @@ -129,6 +129,12 @@ import quickfix.fix44.component.Parties; import quickfix.fix50.MarketDataSnapshotFullRefresh; +/** + * NOTE: There are two MessageTests. One in quickfixj-base, one in + * quickfixj-core, which each test some functionality. This test covers some + * test cases that cannot be tested in the quickfixj-base module due to usage of + * message classes that are generated later in the compile process. + */ public class MessageTest { @Rule @@ -149,13 +155,13 @@ private NewOrderSingle createNewOrderSingle() { HandlInst.AUTOMATED_EXECUTION_INTERVENTION_OK), new Symbol("ORCL"), new Side(Side.BUY), new TransactTime(LocalDateTime.ofEpochSecond(0, 0, ZoneOffset.UTC)), new OrdType(OrdType.LIMIT)); } - + @Test public void testHeaderGroupParsing() throws Exception { final Message message = new Message("8=FIX.4.2\0019=40\00135=A\001" + "627=2\001628=FOO\001628=BAR\001" + "98=0\001384=2\001372=D\001385=R\001372=8\001385=S\00110=228\001", - DataDictionaryTest.getDictionary()); + DataDictionaryTest.getDictionary(), new ValidationSettings()); final quickfix.fix44.Message.Header.NoHops hops = new quickfix.fix44.Message.Header.NoHops(); message.getHeader().getGroup(1, hops); @@ -176,7 +182,7 @@ public void testEmbeddedMessage() throws Exception { report.set(new EncodedTextLen(text.length())); report.set(new EncodedText(text)); - final Message msg = new Message(report.toString(), DataDictionaryTest.getDictionary()); + final Message msg = new Message(report.toString(), DataDictionaryTest.getDictionary(), new ValidationSettings()); assertEquals("embedded order", text, msg.getString(EncodedText.FIELD)); } @@ -186,7 +192,7 @@ private void doTestMessageWithEncodedField(String charset, String text) throws E NewOrderSingle order = createNewOrderSingle(); order.set(new EncodedTextLen(MessageUtils.length(CharsetSupport.getCharsetInstance(), text))); order.set(new EncodedText(text)); - final Message msg = new Message(order.toString(), DataDictionaryTest.getDictionary()); + final Message msg = new Message(order.toString(), DataDictionaryTest.getDictionary(), new ValidationSettings()); assertEquals(charset + " encoded field", text, msg.getString(EncodedText.FIELD)); } finally { CharsetSupport.setDefaultCharset(); @@ -210,7 +216,7 @@ public void testParsing() throws Exception { // checksum is not verified in these tests final Message message = new Message("8=FIX.4.2\0019=40\00135=A\001" + "98=0\001384=2\001372=D\001385=R\001372=8\001385=S\00110=96\001", - DataDictionaryTest.getDictionary()); + DataDictionaryTest.getDictionary(), new ValidationSettings()); assertHeaderField(message, "FIX.4.2", BeginString.FIELD); assertHeaderField(message, "40", BodyLength.FIELD); @@ -242,7 +248,7 @@ public void testParsing2() throws Exception { data += "311=IBM\001"; data += "318=CAD\001"; data += "10=037\001"; - final Message message = new Message(data, DataDictionaryTest.getDictionary()); + final Message message = new Message(data, DataDictionaryTest.getDictionary(), new ValidationSettings()); assertHeaderField(message, "FIX.4.2", BeginString.FIELD); assertHeaderField(message, "76", BodyLength.FIELD); @@ -269,8 +275,8 @@ public void testValidation() throws Exception { final ExecutionReport executionReport = new ExecutionReport(); final DataDictionary dictionary = DataDictionaryTest.getDictionary(); assertNotNull(dictionary); - executionReport.fromString(data, dictionary, true); - dictionary.validate(executionReport); + executionReport.fromString(data, dictionary, new ValidationSettings(), true); + dictionary.validate(executionReport, new ValidationSettings()); } @Test @@ -293,12 +299,12 @@ public void testParseTwice() throws Exception { final ExecutionReport executionReport = new ExecutionReport(); assertNotNull(dictionary); - executionReport.fromString(data1, dictionary, true); - dictionary.validate(executionReport); + executionReport.fromString(data1, dictionary, new ValidationSettings(), true); + dictionary.validate(executionReport, new ValidationSettings()); executionReport.clear(); - executionReport.fromString(data2, dictionary, true); - dictionary.validate(executionReport); + executionReport.fromString(data2, dictionary, new ValidationSettings(), true); + dictionary.validate(executionReport, new ValidationSettings()); } @Test @@ -313,12 +319,12 @@ public void testValidationWithHops() throws Exception { final DataDictionary dictionary = DataDictionaryTest.getDictionary(); assertNotNull(dictionary); - executionReport.fromString(data, dictionary, true); + executionReport.fromString(data, dictionary, new ValidationSettings(), true); final Header.NoHops hops = new Header.NoHops(); hops.set(new HopCompID("FOO")); executionReport.header.addGroup(hops); - dictionary.validate(executionReport); + dictionary.validate(executionReport, new ValidationSettings()); } @Test @@ -331,8 +337,8 @@ public void testAppMessageValidation() throws Exception { final DataDictionary appDictionary = DataDictionaryTest.getDictionary("FIX50.xml"); assertNotNull(sessDictionary); assertNotNull(appDictionary); - mdsfr.fromString(data, sessDictionary, appDictionary, true); - DataDictionary.validate(mdsfr, sessDictionary, appDictionary); + mdsfr.fromString(data, sessDictionary, appDictionary, new ValidationSettings(), true); + DataDictionary.validate(mdsfr, sessDictionary, appDictionary, new ValidationSettings()); } @Test @@ -345,8 +351,9 @@ public void testAppMessageValidationFixLatest() throws Exception { final DataDictionary appDictionary = DataDictionaryTest.getDictionary("FIXLatest.xml"); assertNotNull(sessDictionary); assertNotNull(appDictionary); - mdsfr.fromString(data, sessDictionary, appDictionary, true); - DataDictionary.validate(mdsfr, sessDictionary, appDictionary); + ValidationSettings dds = new ValidationSettings(); + mdsfr.fromString(data, sessDictionary, appDictionary, dds, true); + DataDictionary.validate(mdsfr, sessDictionary, appDictionary, dds); } @Test @@ -358,8 +365,8 @@ public void testAdminMessageValidation() throws Exception { final DataDictionary appDictionary = DataDictionaryTest.getDictionary("FIX50.xml"); assertNotNull(sessionDictionary); assertNotNull(appDictionary); - logon.fromString(data, sessionDictionary, appDictionary, true); - DataDictionary.validate(logon, sessionDictionary, sessionDictionary); + logon.fromString(data, sessionDictionary, appDictionary, new ValidationSettings(), true); + DataDictionary.validate(logon, sessionDictionary, sessionDictionary, new ValidationSettings()); } @Test @@ -415,7 +422,7 @@ public void testInvalidFirstFieldInGroup() throws Exception { news.addGroup(relatedSym); try { - new Message(news.toString(), DataDictionaryTest.getDictionary()); + new Message(news.toString(), DataDictionaryTest.getDictionary(), new ValidationSettings()); } catch (final InvalidMessage e) { // expected } catch (final NullPointerException e) { @@ -429,7 +436,7 @@ public void testRequiredGroupValidation() throws Exception { news.set(new Headline("Test")); final DataDictionary dictionary = DataDictionaryTest.getDictionary(); try { - dictionary.validate(news); + dictionary.validate(news, new ValidationSettings()); fail("no field exception for missing lines group"); } catch (final FieldException e) { // expected @@ -466,7 +473,7 @@ public void testDataFieldWithManualFieldInsertion() throws Exception { m.setInt(RawDataLength.FIELD, data.length()); m.setString(RawData.FIELD, data); assertEquals(1108 + msgType.getValue().length(), m.bodyLength()); - final Message m2 = new Message(m.toString(), dictionary); + final Message m2 = new Message(m.toString(), dictionary, new ValidationSettings()); assertEquals(m.bodyLength(), m2.bodyLength()); } catch (final InvalidMessage e) { fail(e.getMessage()); @@ -545,7 +552,7 @@ public void testFieldOrdering() throws Exception { final DataDictionary dataDictionary = new DataDictionary("FIX44.xml"); final Message message = new DefaultMessageFactory() .create(dataDictionary.getVersion(), "D"); - message.fromString(expectedMessageString, dataDictionary, false); + message.fromString(expectedMessageString, dataDictionary, new ValidationSettings(), false); final String actualMessageString = message.toString(); assertTrue( "wrong field ordering", @@ -777,13 +784,13 @@ public void testFieldWithEqualsCharacter() { try { final DataDictionary dd = DataDictionaryTest.getDictionary(); final Message m = new Message( - "8=FIXT.1.1\0019=369\00135=W\00149=I\00156=F\00134=4\00152=20111021-15:09:16.535\001" + - "262=1319209757316210\00121=2\00155=EUR/USD\001461=RCSXX=0\001268=8\001" + - "269=0\001270=1.38898\001271=2000000\001269=0\001270=1.38897\001271=8000000\001" + - "269=0\001270=1.38854\001271=2000000\001269=1\001270=1.38855\001271=6000000\001" + - "269=1\001270=1.38856\001271=7000000\001269=1\001270=1.38857\001271=3000000\001" + - "269=1\001270=1.38858\001271=9000000\001269=1\001270=1.38859\001271=100000000\00110=51\001", - dd, true); + "8=FIXT.1.1\0019=369\00135=W\00149=I\00156=F\00134=4\00152=20111021-15:09:16.535\001" + + "262=1319209757316210\00121=2\00155=EUR/USD\001461=RCSXX=0\001268=8\001" + + "269=0\001270=1.38898\001271=2000000\001269=0\001270=1.38897\001271=8000000\001" + + "269=0\001270=1.38854\001271=2000000\001269=1\001270=1.38855\001271=6000000\001" + + "269=1\001270=1.38856\001271=7000000\001269=1\001270=1.38857\001271=3000000\001" + + "269=1\001270=1.38858\001271=9000000\001269=1\001270=1.38859\001271=100000000\00110=51\001", + dd, new ValidationSettings(), true); assertEquals(m.getString(461), "RCSXX=0"); final MarketDataSnapshotFullRefresh.NoMDEntries group = new MarketDataSnapshotFullRefresh.NoMDEntries(); m.getGroup(1, group); @@ -802,13 +809,13 @@ public void testMiscFeeType() { try { final DataDictionary dd = DataDictionaryTest.getDictionary(); final Message m = new Message( - "8=FIXT.1.1\0019=369\00135=W\00149=I\00156=F\00134=4\00152=20111021-15:09:16.535\001" + - "262=1319209757316210\00121=2\00155=EUR/USD\001461=RCSXX=0\001268=8\001" + - "269=0\001270=1.38898\001271=2000000\001269=0\001270=1.38897\001271=8000000\001" + - "269=0\001270=1.38854\001271=2000000\001269=1\001270=1.38855\001271=6000000\001" + - "269=1\001270=1.38856\001271=7000000\001269=1\001270=1.38857\001271=3000000\001" + - "269=1\001270=1.38858\001271=9000000\001269=1\001270=1.38859\001271=100000000\00110=51\001", - dd, true); + "8=FIXT.1.1\0019=369\00135=W\00149=I\00156=F\00134=4\00152=20111021-15:09:16.535\001" + + "262=1319209757316210\00121=2\00155=EUR/USD\001461=RCSXX=0\001268=8\001" + + "269=0\001270=1.38898\001271=2000000\001269=0\001270=1.38897\001271=8000000\001" + + "269=0\001270=1.38854\001271=2000000\001269=1\001270=1.38855\001271=6000000\001" + + "269=1\001270=1.38856\001271=7000000\001269=1\001270=1.38857\001271=3000000\001" + + "269=1\001270=1.38858\001271=9000000\001269=1\001270=1.38859\001271=100000000\00110=51\001", + dd, new ValidationSettings(), true); assertEquals(m.getString(461), "RCSXX=0"); final MarketDataSnapshotFullRefresh.NoMDEntries group = new MarketDataSnapshotFullRefresh.NoMDEntries(); m.getGroup(1, group); @@ -879,7 +886,7 @@ public void testRepeatingGroupCount() throws Exception { m1.addGroup(leg2); String s1 = m1.toString(); - Message parsed1 = new Message(s1, DataDictionaryTest.getDictionary()); + Message parsed1 = new Message(s1, DataDictionaryTest.getDictionary(), new ValidationSettings()); assertEquals(s1, parsed1.toString()); assertEquals(2, parsed1.getGroupCount(555)); @@ -902,7 +909,7 @@ public void testRepeatingGroupCount() throws Exception { String s2 = m2.toString(); // do not use validation to parse full message // regardless of errors in message structure - Message parsed2 = new Message(s2, DataDictionaryTest.getDictionary(), false); + Message parsed2 = new Message(s2, DataDictionaryTest.getDictionary(), new ValidationSettings(), false); assertEquals(s2, parsed2.toString()); assertEquals(2, parsed2.getGroupCount(555)); @@ -953,22 +960,23 @@ public void testUnknownFieldsInRepeatingGroupsAndValidation() throws Exception { m1.addGroup(sides); String s1 = m1.toString(); + ValidationSettings validationSettings = new ValidationSettings(); DataDictionary dictionary = new DataDictionary(DataDictionaryTest.getDictionary()); // parsing without validation should succeed - Message parsed1 = new Message(s1, dictionary, false); + Message parsed1 = new Message(s1, dictionary, validationSettings,false); // validation should fail int failingTag = 0; try { - dictionary.validate(parsed1); + dictionary.validate(parsed1, validationSettings); } catch (FieldException e) { failingTag = e.getField(); } assertEquals(10000, failingTag); // but without checking user-defined fields, validation should succeed - dictionary.setCheckUserDefinedFields(false); - dictionary.validate(parsed1); + validationSettings.setCheckUserDefinedFields(false); + dictionary.validate(parsed1, validationSettings); assertEquals(s1, parsed1.toString()); assertEquals(2, parsed1.getGroupCount(555)); @@ -990,20 +998,21 @@ public void testUnknownFieldsInRepeatingGroupsAndValidation() throws Exception { String s2 = m2.toString(); DataDictionary dictionary = new DataDictionary(DataDictionaryTest.getDictionary()); // parsing without validation should succeed - Message parsed2 = new Message(s2, dictionary, false); + Message parsed2 = new Message(s2, dictionary, new ValidationSettings(), false); // validation should fail int failingTag = 0; try { - dictionary.validate(parsed2); + dictionary.validate(parsed2, new ValidationSettings()); } catch (FieldException e) { failingTag = e.getField(); } assertEquals(Text.FIELD, failingTag); // but without checking for unknown message fields, validation should succeed - dictionary.setAllowUnknownMessageFields(true); - dictionary.validate(parsed2); + ValidationSettings validationSettings = new ValidationSettings(); + validationSettings.setAllowUnknownMessageFields(true); + dictionary.validate(parsed2, validationSettings); assertEquals(s2, parsed2.toString()); assertEquals(2, parsed2.getGroupCount(555)); @@ -1041,10 +1050,11 @@ public void testInvalidFieldInGroup() throws Exception { responseMessage.setField(resultCode); DataDictionary dd = new DataDictionary(DataDictionaryTest.getDictionary()); + ValidationSettings validationSettings = new ValidationSettings(); int tagNo = 0; try { - dd.validate(responseMessage, true); + dd.validate(responseMessage, true, validationSettings); } catch (FieldException e) { tagNo = e.getField(); } @@ -1052,9 +1062,9 @@ public void testInvalidFieldInGroup() throws Exception { // (which is the first field after the invalid 297 field) assertEquals(QuoteAckStatus.FIELD, tagNo); - Message msg2 = new Message(responseMessage.toString(), dd); + Message msg2 = new Message(responseMessage.toString(), dd, validationSettings); try { - dd.validate(msg2, true); + dd.validate(msg2, true, validationSettings); } catch (FieldException e) { tagNo = e.getField(); } @@ -1063,7 +1073,7 @@ public void testInvalidFieldInGroup() throws Exception { assertEquals(QuoteAckStatus.FIELD, tagNo); // parse message again without validation - msg2 = new Message(responseMessage.toString(), dd, false); + msg2 = new Message(responseMessage.toString(), dd, validationSettings, false); assertEquals(responseMessage.toString(), msg2.toString()); Group noRelatedSymGroup = new quickfix.fix44.DerivativeSecurityList.NoRelatedSym(); Group group = responseMessage.getGroup(1, noRelatedSymGroup); @@ -1087,10 +1097,11 @@ public void testNestedRepeatingGroup() quickfix.fix44.NewOrderSingle nos = new quickfix.fix44.NewOrderSingle(); // using custom dictionary with user-defined tag 22000 final DataDictionary dataDictionary = new DataDictionary("FIX44_Custom_Test.xml"); - dataDictionary.setCheckUserDefinedFields(false); - nos.fromString(newOrdersSingleString.replaceAll("\\|", "\001"), dataDictionary, true); + ValidationSettings validationSettings = new ValidationSettings(); + validationSettings.setCheckUserDefinedFields(false); + nos.fromString(newOrdersSingleString.replaceAll("\\|", "\001"), dataDictionary, validationSettings, true); assertNull(nos.getException()); - dataDictionary.validate(nos); + dataDictionary.validate(nos, validationSettings); // defined tag should be set on the message assertTrue(nos.isSetField(22000)); @@ -1115,10 +1126,11 @@ public void testUnknownTagBeforeFirstFieldInRepeatingGroup() quickfix.fix44.NewOrderSingle nos = new quickfix.fix44.NewOrderSingle(); final DataDictionary dataDictionary = new DataDictionary(DataDictionaryTest.getDictionary()); - dataDictionary.setCheckUserDefinedFields(false); + ValidationSettings validationSettings = new ValidationSettings(); + validationSettings.setCheckUserDefinedFields(false); // When - nos.fromString(newOrdersSingleString.replaceAll("\\|", "\001"), dataDictionary, true); + nos.fromString(newOrdersSingleString.replaceAll("\\|", "\001"), dataDictionary, validationSettings, true); // Then FieldException e = nos.getException(); @@ -1141,10 +1153,11 @@ public void testNestedRepeatingSubGroup() quickfix.fix44.NewOrderSingle nos = new quickfix.fix44.NewOrderSingle(); // using custom dictionary with user-defined tag 22000 final DataDictionary dataDictionary = new DataDictionary("FIX44_Custom_Test.xml"); - dataDictionary.setCheckUserDefinedFields(false); - nos.fromString(newOrdersSingleString.replaceAll("\\|", "\001"), dataDictionary, true); + ValidationSettings validationSettings = new ValidationSettings(); + validationSettings.setCheckUserDefinedFields(false); + nos.fromString(newOrdersSingleString.replaceAll("\\|", "\001"), dataDictionary, validationSettings, true); assertNull(nos.getException()); - dataDictionary.validate(nos); + dataDictionary.validate(nos, validationSettings); // defined tag should be set on the message assertTrue(nos.isSetField(22000)); @@ -1191,9 +1204,10 @@ private void testRepeatingGroupCountForFieldOrder(int fieldOrder[]) throws Excep */ String s = tcr.toString(); DataDictionary dictionary = new DataDictionary(DataDictionaryTest.getDictionary()); - dictionary.setCheckUnorderedGroupFields(false); + ValidationSettings validationSettings = new ValidationSettings(); + validationSettings.setCheckUnorderedGroupFields(false); // without checking order of repeating group it should work - Message parsed = new Message(s, dictionary); + Message parsed = new Message(s, dictionary, validationSettings); FieldException exception = parsed.getException(); assertNull(exception); @@ -1201,7 +1215,7 @@ private void testRepeatingGroupCountForFieldOrder(int fieldOrder[]) throws Excep dictionary = new DataDictionary(DataDictionaryTest.getDictionary()); // when checking order of repeating group, an error should be reported - parsed = new Message(s, dictionary); + parsed = new Message(s, dictionary, new ValidationSettings()); exception = parsed.getException(); assertEquals(654, exception.getField()); // but we still should have the repeating group set and not ignore it @@ -1212,11 +1226,12 @@ private void testRepeatingGroupCountForFieldOrder(int fieldOrder[]) throws Excep @Test public void testRepeatingGroupCountWithNonIntegerValues() throws Exception { DataDictionary dictionary = new DataDictionary(DataDictionaryTest.getDictionary()); + ValidationSettings validationSettings = new ValidationSettings(); Message ioi = new quickfix.fix50.IOI(); ioi.setString(quickfix.field.NoPartyIDs.FIELD, "abc"); final String invalidCountMessage = ioi.toString(); try { - Message message = new Message(invalidCountMessage, dictionary); + Message message = new Message(invalidCountMessage, dictionary, validationSettings); } catch (final InvalidMessage im) { assertNotNull("InvalidMessage correctly thrown", im); } catch (final Throwable e) { @@ -1244,7 +1259,6 @@ public void testIfMessageHeaderIsOverwritten() { @Test public void shouldConvertToXmlWhenDataDictionaryLoadedWithExternalDTD() throws ConfigError { DataDictionary dataDictionary = new DataDictionary("FIX_External_DTD.xml", DocumentBuilderFactory::newInstance); - Message message = new Message(); message.setString(Account.FIELD, "test-account"); @@ -1279,6 +1293,7 @@ public void shouldConvertToXMLWithIndent() { public void testValidateFieldsOutOfOrderFIXT11() throws Exception { final DataDictionary sessDictionary = DataDictionaryTest.getDictionary("FIXT11.xml"); final DataDictionary appDictionary = DataDictionaryTest.getDictionary("FIX50SP2.xml"); + final ValidationSettings dds = new ValidationSettings(); assertNotNull(sessDictionary); assertNotNull(appDictionary); assertNotEquals(appDictionary.getVersion(), sessDictionary.getVersion()); @@ -1295,15 +1310,14 @@ public void testValidateFieldsOutOfOrderFIXT11() throws Exception { "54=2\u0001453=1\u0001448=338-3\u0001447=D\u0001452=1\u00011=1040445\u0001576=1\u0001577=0\u000111=7533509260093757876\u0001" + "10=129\u0001"; final TradeCaptureReport tcrOrdered = new TradeCaptureReport(); - tcrOrdered.fromString(orderedData, sessDictionary, appDictionary, true); - DataDictionary.validate(tcrOrdered, sessDictionary, appDictionary); + tcrOrdered.fromString(orderedData, sessDictionary, appDictionary, dds, true); + DataDictionary.validate(tcrOrdered, sessDictionary, appDictionary, dds); // As this is our reference message created with all validations switched on, make sure some message components // are as expected assertEquals(tcrOrdered.getHeader().getGroupCount(NoHops.FIELD), 2); assertEquals(tcrOrdered.getGroupCount(NoSides.FIELD), 2); - sessDictionary.setCheckFieldsOutOfOrder(false); - appDictionary.setCheckFieldsOutOfOrder(false); + dds.setCheckFieldsOutOfOrder(false); String unorderedData = "8=FIXT.1.1\u00019=561\u000135=AE\u0001" + "15=AUD\u000122=4\u000131=27\u000132=5000.000000000000\u000148=AU000000ANZ3\u000155=ANZ\u000160=20220210-02:43:27.796\u000164=20220214\u000175=20220210\u0001106=4075\u0001167=CS\u0001381=135000\u0001461=Exxxxx\u0001487=0\u0001762=1\u0001880=7533509260093686098:0#NORMAL#1644451200000000000\u00011003=1120000338\u00011015=0\u00011301=XASX\u0001" + @@ -1315,8 +1329,8 @@ public void testValidateFieldsOutOfOrderFIXT11() throws Exception { "34=545\u000149=SENDER\u000152=20220210-02:44:00.820\u000156=TARGET\u0001115=ON_BHEHALF\u00011128=9\u0001" + "10=129\u0001"; TradeCaptureReport tcrUnOrdered = new TradeCaptureReport(); - tcrUnOrdered.fromString(unorderedData, sessDictionary, appDictionary, true); - DataDictionary.validate(tcrUnOrdered, sessDictionary, appDictionary); + tcrUnOrdered.fromString(unorderedData, sessDictionary, appDictionary, dds, true); + DataDictionary.validate(tcrUnOrdered, sessDictionary, appDictionary, dds); assertEquals(tcrOrdered.toString(), tcrUnOrdered.toString()); @@ -1330,8 +1344,8 @@ public void testValidateFieldsOutOfOrderFIXT11() throws Exception { "627=2\u0001628=HOPID1\u0001629=20220414-15:22:54\u0001628=HOPID2\u0001629=20220414-15:22:54\u0001" + "10=129\u0001"; tcrUnOrdered = new TradeCaptureReport(); - tcrUnOrdered.fromString(unorderedData, sessDictionary, appDictionary, true); - DataDictionary.validate(tcrUnOrdered, sessDictionary, appDictionary); + tcrUnOrdered.fromString(unorderedData, sessDictionary, appDictionary, dds, true); + DataDictionary.validate(tcrUnOrdered, sessDictionary, appDictionary, dds); assertEquals(tcrOrdered.toString(), tcrUnOrdered.toString()); @@ -1347,8 +1361,8 @@ public void testValidateFieldsOutOfOrderFIXT11() throws Exception { "627=2\u0001628=HOPID1\u0001629=20220414-15:22:54\u0001628=HOPID2\u0001629=20220414-15:22:54\u0001" + "10=129\u0001"; tcrUnOrdered = new TradeCaptureReport(); - tcrUnOrdered.fromString(unorderedData, sessDictionary, appDictionary, true); - DataDictionary.validate(tcrUnOrdered, sessDictionary, appDictionary); + tcrUnOrdered.fromString(unorderedData, sessDictionary, appDictionary, dds, true); + DataDictionary.validate(tcrUnOrdered, sessDictionary, appDictionary, dds); assertEquals(tcrOrdered.toString(), tcrUnOrdered.toString()); @@ -1357,6 +1371,7 @@ public void testValidateFieldsOutOfOrderFIXT11() throws Exception { @Test public void testValidateFieldsOutOfOrderPreFIXT11() throws Exception { final DataDictionary sessDictionary = DataDictionaryTest.getDictionary("FIX44.xml"); + final ValidationSettings dds = new ValidationSettings(); assertNotNull(sessDictionary); final String orderedData = @@ -1369,8 +1384,8 @@ public void testValidateFieldsOutOfOrderPreFIXT11() throws Exception { + "54=2\u000137=OrderID2\u000111=7533509260093757876\u0001453=1\u0001448=338-3\u0001447=D\u0001452=1\u00011=1040445\u0001576=1\u0001577=0\u0001" + "10=191\u0001"; final TradeCaptureReport tcrOrdered = new TradeCaptureReport(); - tcrOrdered.fromString(orderedData, sessDictionary, true); - DataDictionary.validate(tcrOrdered, sessDictionary, sessDictionary); + tcrOrdered.fromString(orderedData, sessDictionary, dds, true); + DataDictionary.validate(tcrOrdered, sessDictionary, sessDictionary, dds); // As this is our reference message created with all validations switched on, // make sure some message components @@ -1378,7 +1393,7 @@ public void testValidateFieldsOutOfOrderPreFIXT11() throws Exception { assertEquals(tcrOrdered.getHeader().getGroupCount(NoHops.FIELD), 2); assertEquals(tcrOrdered.getGroupCount(NoSides.FIELD), 2); - sessDictionary.setCheckFieldsOutOfOrder(false); + dds.setCheckFieldsOutOfOrder(false); String unorderedData = "8=FIX.4.4\u00019=551\u000135=AE\u0001" + "22=4\u000131=27\u000132=5000.000000000000\u000148=AU000000ANZ3\u000155=ANZ\u000160=20220210-02:43:27.796\u000164=20220214\u000175=20220210\u0001106=4075\u0001167=CS\u0001461=Exxxxx\u0001487=0\u0001570=N\u0001571=TradeReportID\u0001762=1\u0001880=7533509260093686098:0#NORMAL#1644451200000000000\u0001" @@ -1390,8 +1405,8 @@ public void testValidateFieldsOutOfOrderPreFIXT11() throws Exception { + "34=545\u000149=SENDER\u000152=20220210-02:44:00.820\u000156=TARGET\u0001115=ON_BHEHALF\u0001" + "10=191\u0001"; TradeCaptureReport tcrUnOrdered = new TradeCaptureReport(); - tcrUnOrdered.fromString(unorderedData, sessDictionary, true); - DataDictionary.validate(tcrUnOrdered, sessDictionary, sessDictionary); + tcrUnOrdered.fromString(unorderedData, sessDictionary, dds, true); + DataDictionary.validate(tcrUnOrdered, sessDictionary, sessDictionary, dds); assertEquals(tcrOrdered.toString(), tcrUnOrdered.toString()); @@ -1406,8 +1421,8 @@ public void testValidateFieldsOutOfOrderPreFIXT11() throws Exception { + "10=191\u0001"; tcrUnOrdered = new TradeCaptureReport(); - tcrUnOrdered.fromString(unorderedData, sessDictionary, true); - DataDictionary.validate(tcrUnOrdered, sessDictionary, sessDictionary); + tcrUnOrdered.fromString(unorderedData, sessDictionary, dds, true); + DataDictionary.validate(tcrUnOrdered, sessDictionary, sessDictionary, dds); assertEquals(tcrOrdered.toString(), tcrUnOrdered.toString()); @@ -1424,11 +1439,10 @@ public void testValidateFieldsOutOfOrderPreFIXT11() throws Exception { + "627=2\u0001628=HOPID1\u0001629=20220414-15:22:54\u0001628=HOPID2\u0001629=20220414-15:22:54\u0001" + "10=191\u0001"; tcrUnOrdered = new TradeCaptureReport(); - tcrUnOrdered.fromString(unorderedData, sessDictionary, true); - DataDictionary.validate(tcrUnOrdered, sessDictionary, sessDictionary); + tcrUnOrdered.fromString(unorderedData, sessDictionary, dds, true); + DataDictionary.validate(tcrUnOrdered, sessDictionary, sessDictionary, dds); assertEquals(tcrOrdered.toString(), tcrUnOrdered.toString()); - } private void assertHeaderField(Message message, String expectedValue, int field) diff --git a/quickfixj-core/src/test/java/quickfix/MessageUtilsTest.java b/quickfixj-core/src/test/java/quickfix/MessageUtilsTest.java index 803b9d6d5e..14e4aadf43 100644 --- a/quickfixj-core/src/test/java/quickfix/MessageUtilsTest.java +++ b/quickfixj-core/src/test/java/quickfix/MessageUtilsTest.java @@ -104,8 +104,8 @@ public void testLegacyParse() throws Exception { "44=15\00159=1\0016=0\001453=3\001448=AAA35791\001447=D\001452=3\001448=8\001" + "447=D\001452=4\001448=FIX11\001447=D\001452=36\00160=20060320-03:34:29\00110=169\001"; - Message message = MessageUtils.parse(new quickfix.fix40.MessageFactory(), DataDictionaryTest.getDictionary(), data); - assertThat(message, notNullValue()); + Message message = MessageUtils.parse(new quickfix.fix40.MessageFactory(), DataDictionaryTest.getDictionary(), new ValidationSettings(), data); + assertThat(message, is(notNullValue())); } @Test diff --git a/quickfixj-core/src/test/java/quickfix/RepeatingGroupTest.java b/quickfixj-core/src/test/java/quickfix/RepeatingGroupTest.java index a6cf3ea396..cc189cb770 100644 --- a/quickfixj-core/src/test/java/quickfix/RepeatingGroupTest.java +++ b/quickfixj-core/src/test/java/quickfix/RepeatingGroupTest.java @@ -300,26 +300,28 @@ public void testSettingGettingGroupByReusingGroup() throws FieldNotFound { // Testing Message validation private static DataDictionary defaultDataDictionary = null; - private static DataDictionary defaultDataDictionaryWithIgnoreOutOfOrder = null; + private static ValidationSettings defaultDDSettings = null; + private static ValidationSettings ignoreOutOfOrderSettings = null; private static DataDictionary customDataDictionary = null; private final DefaultMessageFactory messageFactory = new DefaultMessageFactory(); static { try { defaultDataDictionary = new DataDictionary("FIX44.xml"); - defaultDataDictionaryWithIgnoreOutOfOrder = new DataDictionary("FIX44.xml"); - defaultDataDictionaryWithIgnoreOutOfOrder.setCheckUnorderedGroupFields(false); + defaultDDSettings = new ValidationSettings(); + ignoreOutOfOrderSettings = new ValidationSettings(); + ignoreOutOfOrderSettings.setCheckUnorderedGroupFields(false); customDataDictionary = new DataDictionary("FIX44_Custom_Test.xml"); } catch (final ConfigError e) { e.printStackTrace(); } } - private Message buildValidatedMessage(String sourceFIXString, DataDictionary dd) + private Message buildValidatedMessage(String sourceFIXString, DataDictionary dd, ValidationSettings validationSettings) throws InvalidMessage { final Message message = messageFactory.create(MessageUtils.getStringField(sourceFIXString, BeginString.FIELD), MessageUtils.getMessageType(sourceFIXString)); - message.fromString(sourceFIXString, dd, true); + message.fromString(sourceFIXString, dd, validationSettings, true); return message; } @@ -340,7 +342,7 @@ public void testValidationWithNestedGroupAndStandardFields() throws InvalidMessa final String sourceFIXString = quoteRequest.toString(); final quickfix.fix44.QuoteRequest validatedMessage = (quickfix.fix44.QuoteRequest) buildValidatedMessage( - sourceFIXString, defaultDataDictionary); + sourceFIXString, defaultDataDictionary, defaultDDSettings); String validateFIXString = null; if (validatedMessage != null) { validateFIXString = validatedMessage.toString(); @@ -367,7 +369,7 @@ public void testValidationWithNestedGroupAndStandardFieldsFIX50SP2() throws Inva final String sourceFIXString = quoteRequest.toString(); final DataDictionary fix50sp2DataDictionary = new DataDictionary("FIX50SP2.xml"); final quickfix.fix50sp2.QuoteRequest validatedMessage = (quickfix.fix50sp2.QuoteRequest) messageFactory.create(FixVersions.FIX50SP2, QuoteRequest.MSGTYPE); - validatedMessage.fromString(sourceFIXString, fix50sp2DataDictionary, true); + validatedMessage.fromString(sourceFIXString, fix50sp2DataDictionary, new ValidationSettings(), true); String validateFIXString = validatedMessage.toString(); @@ -393,7 +395,7 @@ public void testValidationWithNestedGroupAndStandardFieldsFIXLatest() throws Inv final String sourceFIXString = quoteRequest.toString(); final DataDictionary fixDataDictionary = new DataDictionary("FIXLatest.xml"); final quickfix.fixlatest.QuoteRequest validatedMessage = (quickfix.fixlatest.QuoteRequest) messageFactory.create(FixVersions.FIXLATEST, QuoteRequest.MSGTYPE); - validatedMessage.fromString(sourceFIXString, fixDataDictionary, true); + validatedMessage.fromString(sourceFIXString, fixDataDictionary, new ValidationSettings(), true); String validateFIXString = validatedMessage.toString(); @@ -416,7 +418,7 @@ public void testValidationWithNestedGroupAndStandardFieldsWithoutDelimiter() thr final String sourceFIXString = quoteRequest.toString(); - Message buildValidatedMessage = buildValidatedMessage(sourceFIXString, defaultDataDictionary); + Message buildValidatedMessage = buildValidatedMessage(sourceFIXString, defaultDataDictionary, defaultDDSettings); assertEquals("The group 146 must set the delimiter field 55", buildValidatedMessage.getException().getMessage()); } @@ -451,7 +453,7 @@ public void testGroupFieldsOrderWithCustomDataDictionary() throws InvalidMessage final String sourceFIXString = quoteRequest.toString(); final quickfix.fix44.QuoteRequest validatedMessage = (quickfix.fix44.QuoteRequest) buildValidatedMessage( - sourceFIXString, customDataDictionary); + sourceFIXString, customDataDictionary, new ValidationSettings()); assertNull("Invalid message", validatedMessage.getException()); @@ -466,9 +468,9 @@ public void testOutOfOrderGroupMembersDelimiterField() throws Exception { "8=FIX.4.4\0019=0\00135=D\00134=2\00149=TW\00152=