From ab0665a041c9799786465dd104487dbcf39121a2 Mon Sep 17 00:00:00 2001 From: Borisas Bursteinas Date: Sat, 29 Jun 2024 17:38:56 +0100 Subject: [PATCH 1/3] extra logging first draft --- .../main/java/quickfix/DataDictionary.java | 21 ++++++++++++++++--- .../java/quickfix/ValidationSettings.java | 10 +++++++++ .../java/quickfix/ValidationSettingsTest.java | 2 ++ .../java/quickfix/DefaultSessionFactory.java | 5 +++++ .../src/main/java/quickfix/Session.java | 2 ++ .../quickfix/DefaultSessionFactoryTest.java | 3 +++ 6 files changed, 40 insertions(+), 3 deletions(-) diff --git a/quickfixj-base/src/main/java/quickfix/DataDictionary.java b/quickfixj-base/src/main/java/quickfix/DataDictionary.java index 012c63ccf6..aa9855b54d 100644 --- a/quickfixj-base/src/main/java/quickfix/DataDictionary.java +++ b/quickfixj-base/src/main/java/quickfix/DataDictionary.java @@ -19,6 +19,8 @@ package quickfix; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; @@ -73,6 +75,8 @@ public class DataDictionary { private static final String JDK_DOCUMENT_BUILDER_FACTORY_NAME = "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl"; private static final Supplier DEFAULT_DOCUMENT_BUILDER_FACTORY_SUPPLIER = createDocumentBuilderFactorySupplier(); + protected static final Logger LOG = LoggerFactory.getLogger(DataDictionary.class); + private static Supplier createDocumentBuilderFactorySupplier() { return () -> { DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); @@ -670,7 +674,7 @@ private void iterate(ValidationSettings settings, FieldMap map, String msgType, if (hasVersion) { checkValidFormat(settings, field); - checkValue(field); + checkValue(settings, field); } if (beginString != null) { @@ -720,8 +724,15 @@ boolean checkFieldFailure(ValidationSettings settings, int field, boolean messag boolean fail; if (field < USER_DEFINED_TAG_MIN) { fail = !messageField && !settings.allowUnknownMessageFields; + if (settings.fieldValidationLogging && !messageField && settings.allowUnknownMessageFields) { + LOG.warn("Unknown Message Field {} detected", field); + } } else { fail = !messageField && settings.checkUserDefinedFields; + if (settings.fieldValidationLogging && !messageField && settings.checkUserDefinedFields) { + LOG.warn("Unknown User Defined Field {} detected", field); + } + } return fail; } @@ -788,10 +799,14 @@ private void checkValidFormat(ValidationSettings settings, StringField field) th } } - private void checkValue(StringField field) throws IncorrectTagValue { + private void checkValue(ValidationSettings settings, StringField field) throws IncorrectTagValue { int tag = field.getField(); if (hasFieldValue(tag) && !isFieldValue(tag, field.getValue())) { - throw new IncorrectTagValue(tag); + if (settings.fieldValidationLogging) { + LOG.warn("Unknown Enum value {} for tag {} is detected", field.getValue(), tag); + } else { + throw new IncorrectTagValue(tag); + } } } diff --git a/quickfixj-base/src/main/java/quickfix/ValidationSettings.java b/quickfixj-base/src/main/java/quickfix/ValidationSettings.java index f2d242eac6..e52cdcad67 100644 --- a/quickfixj-base/src/main/java/quickfix/ValidationSettings.java +++ b/quickfixj-base/src/main/java/quickfix/ValidationSettings.java @@ -25,6 +25,7 @@ public class ValidationSettings { boolean checkUserDefinedFields = true; boolean checkUnorderedGroupFields = true; boolean allowUnknownMessageFields = false; + boolean fieldValidationLogging = false; public ValidationSettings() {} @@ -34,6 +35,7 @@ public ValidationSettings(ValidationSettings validationSettings) { this.checkUserDefinedFields = validationSettings.checkUserDefinedFields; this.checkUnorderedGroupFields = validationSettings.checkUnorderedGroupFields; this.allowUnknownMessageFields = validationSettings.allowUnknownMessageFields; + this.fieldValidationLogging = validationSettings.fieldValidationLogging; } /** @@ -65,6 +67,10 @@ public boolean isAllowUnknownMessageFields() { return allowUnknownMessageFields; } + public boolean isFieldValidationLogging() { + return fieldValidationLogging; + } + /** * Controls whether group fields are in the same order * @@ -95,4 +101,8 @@ public void setCheckUserDefinedFields(boolean flag) { public void setAllowUnknownMessageFields(boolean allowUnknownFields) { allowUnknownMessageFields = allowUnknownFields; } + + public void setFieldValidationLogging(boolean fieldValidation) { + fieldValidationLogging = fieldValidation; + } } diff --git a/quickfixj-base/src/test/java/quickfix/ValidationSettingsTest.java b/quickfixj-base/src/test/java/quickfix/ValidationSettingsTest.java index a46eefff8d..faffab722f 100644 --- a/quickfixj-base/src/test/java/quickfix/ValidationSettingsTest.java +++ b/quickfixj-base/src/test/java/quickfix/ValidationSettingsTest.java @@ -14,6 +14,7 @@ public void copyConstructor_retains_settings() { validationSettings.setCheckFieldsOutOfOrder(false); validationSettings.setCheckUnorderedGroupFields(false); validationSettings.setCheckUserDefinedFields(false); + validationSettings.setFieldValidationLogging(true); ValidationSettings validationSettingsCopy = new ValidationSettings(validationSettings); @@ -22,5 +23,6 @@ public void copyConstructor_retains_settings() { assertEquals(validationSettingsCopy.isCheckFieldsOutOfOrder(), validationSettings.isCheckFieldsOutOfOrder()); assertEquals(validationSettingsCopy.isCheckUnorderedGroupFields(), validationSettings.isCheckUnorderedGroupFields()); assertEquals(validationSettingsCopy.isCheckUserDefinedFields(), validationSettings.isCheckUserDefinedFields()); + assertEquals(validationSettingsCopy.isFieldValidationLogging(), validationSettings.isFieldValidationLogging()); } } diff --git a/quickfixj-core/src/main/java/quickfix/DefaultSessionFactory.java b/quickfixj-core/src/main/java/quickfix/DefaultSessionFactory.java index 9da621fe7e..37241d7ad7 100644 --- a/quickfixj-core/src/main/java/quickfix/DefaultSessionFactory.java +++ b/quickfixj-core/src/main/java/quickfix/DefaultSessionFactory.java @@ -314,6 +314,11 @@ private ValidationSettings createValidationSettings(SessionID sessionID, Session Session.SETTING_ALLOW_UNKNOWN_MSG_FIELDS)); } + if (settings.isSetting(sessionID, Session.SETTING_FIELD_VALIDATION_LOGGING)) { + validationSettings.setFieldValidationLogging(settings.getBool(sessionID, + Session.SETTING_FIELD_VALIDATION_LOGGING)); + } + return validationSettings; } diff --git a/quickfixj-core/src/main/java/quickfix/Session.java b/quickfixj-core/src/main/java/quickfix/Session.java index 847f5c2885..d89cd40ab1 100644 --- a/quickfixj-core/src/main/java/quickfix/Session.java +++ b/quickfixj-core/src/main/java/quickfix/Session.java @@ -323,6 +323,8 @@ public class Session implements Closeable { */ public static final String SETTING_ALLOW_UNKNOWN_MSG_FIELDS = "AllowUnknownMsgFields"; + public static final String SETTING_FIELD_VALIDATION_LOGGING = "FieldValidationLogging"; + public static final String SETTING_DEFAULT_APPL_VER_ID = "DefaultApplVerID"; /** diff --git a/quickfixj-core/src/test/java/quickfix/DefaultSessionFactoryTest.java b/quickfixj-core/src/test/java/quickfix/DefaultSessionFactoryTest.java index 57eb7bea7b..b0ceeac76b 100644 --- a/quickfixj-core/src/test/java/quickfix/DefaultSessionFactoryTest.java +++ b/quickfixj-core/src/test/java/quickfix/DefaultSessionFactoryTest.java @@ -100,6 +100,7 @@ public void testFixtDataDictionaryConfiguration() throws Exception { 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"); + settings.setString(sessionID, Session.SETTING_FIELD_VALIDATION_LOGGING, "Y"); try (Session session = factory.create(sessionID, settings)) { @@ -113,6 +114,7 @@ public void testFixtDataDictionaryConfiguration() throws Exception { is(notNullValue())); assertTrue(session.getValidationSettings().isAllowUnknownMessageFields()); assertFalse(session.getValidationSettings().isCheckUnorderedGroupFields()); + assertTrue(session.getValidationSettings().isFieldValidationLogging()); } } @@ -142,6 +144,7 @@ public void testFixtDataDictionaryConfigurationWithDefaultAppDataDictionary() th is(notNullValue())); assertTrue(session.getValidationSettings().isAllowUnknownMessageFields()); assertFalse(session.getValidationSettings().isCheckUnorderedGroupFields()); + assertFalse(session.getValidationSettings().isFieldValidationLogging()); } } From ba6675da870430234990b1319727f149d9406982 Mon Sep 17 00:00:00 2001 From: Borisas Bursteinas Date: Mon, 1 Jul 2024 19:09:53 +0100 Subject: [PATCH 2/3] manual update --- .../doc/usermanual/usage/configuration.html | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/quickfixj-core/src/main/doc/usermanual/usage/configuration.html b/quickfixj-core/src/main/doc/usermanual/usage/configuration.html index da01eb46e2..95d9dab4d5 100644 --- a/quickfixj-core/src/main/doc/usermanual/usage/configuration.html +++ b/quickfixj-core/src/main/doc/usermanual/usage/configuration.html @@ -406,6 +406,24 @@

QuickFIX Settings

N N + + FieldValidationLogging + If set to Y, then +
    +
  • + if AllowUnknownMsgFields=Y, will print information about detected unknown non user defined fields (field with tag < 5000) and these fields will not be rejected +
  • +
  • + if ValidateUserDefinedFields=Y, will print information about detected unknown user defined fields (field with tag >= 5000) and these fields will not be rejected +
  • +
  • + will print information about fields with unknown Enum values and these fields will not be rejected. +
  • +
+ Y
+ N + N + CheckCompID If set to Y, messages must be received from the counterparty with the correct SenderCompID and TargetCompID. From 46676682f17f689131d5266f2dc65bbd28fe358a Mon Sep 17 00:00:00 2001 From: Borisas Bursteinas Date: Sat, 26 Oct 2024 18:54:14 +0100 Subject: [PATCH 3/3] logging in DataDictionary through callback --- .../main/java/quickfix/DataDictionary.java | 19 +++++++++++++++++-- .../src/main/java/quickfix/Session.java | 3 ++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/quickfixj-base/src/main/java/quickfix/DataDictionary.java b/quickfixj-base/src/main/java/quickfix/DataDictionary.java index aa9855b54d..b12df32eac 100644 --- a/quickfixj-base/src/main/java/quickfix/DataDictionary.java +++ b/quickfixj-base/src/main/java/quickfix/DataDictionary.java @@ -49,6 +49,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Consumer; import java.util.function.Supplier; import javax.xml.XMLConstants; @@ -112,6 +113,7 @@ private static Supplier createDocumentBuilderFactorySupp private final StringIntegerMap groups = new StringIntegerMap<>(); private final Map components = new HashMap<>(); private int[] orderedFieldsArray; + private static Consumer callback = LOG::warn; private DataDictionary() { } @@ -620,11 +622,23 @@ public void validate(Message message, ValidationSettings settings) throws Incorr * @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, ValidationSettings settings, Consumer customCallback) throws IncorrectTagValue, + FieldNotFound, IncorrectDataFormat { + validate(message, bodyOnly ? null : this, this, settings, customCallback); + } + public void validate(Message message, boolean bodyOnly, ValidationSettings settings) throws IncorrectTagValue, FieldNotFound, IncorrectDataFormat { validate(message, bodyOnly ? null : this, this, settings); } + static void validate(Message message, DataDictionary sessionDataDictionary, + DataDictionary applicationDataDictionary, ValidationSettings settings, Consumer sessionCallback) throws IncorrectTagValue, FieldNotFound, + IncorrectDataFormat { + callback = sessionCallback; + validate(message, sessionDataDictionary, applicationDataDictionary, settings); + } + static void validate(Message message, DataDictionary sessionDataDictionary, DataDictionary applicationDataDictionary, ValidationSettings settings) throws IncorrectTagValue, FieldNotFound, IncorrectDataFormat { @@ -724,13 +738,14 @@ boolean checkFieldFailure(ValidationSettings settings, int field, boolean messag boolean fail; if (field < USER_DEFINED_TAG_MIN) { fail = !messageField && !settings.allowUnknownMessageFields; + if (settings.fieldValidationLogging && !messageField && settings.allowUnknownMessageFields) { - LOG.warn("Unknown Message Field {} detected", field); + callback.accept("Unknown Message Field " + field + " detected"); } } else { fail = !messageField && settings.checkUserDefinedFields; if (settings.fieldValidationLogging && !messageField && settings.checkUserDefinedFields) { - LOG.warn("Unknown User Defined Field {} detected", field); + callback.accept("Unknown User Defined Field " + field + " detected"); } } diff --git a/quickfixj-core/src/main/java/quickfix/Session.java b/quickfixj-core/src/main/java/quickfix/Session.java index d89cd40ab1..acbc92c0bd 100644 --- a/quickfixj-core/src/main/java/quickfix/Session.java +++ b/quickfixj-core/src/main/java/quickfix/Session.java @@ -1063,7 +1063,8 @@ 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, validationSettings); + applicationDataDictionary, validationSettings, + text -> getLog().onWarnEvent("incoming message " + text + ": " + getMessageToLog(message))); } catch (final IncorrectTagValue e) { if (rejectInvalidMessage) { throw e;