Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions quickfixj-base/src/main/java/quickfix/DataDictionary.java
Original file line number Diff line number Diff line change
Expand Up @@ -670,7 +670,7 @@ private void iterate(ValidationSettings settings, FieldMap map, String msgType,

if (hasVersion) {
checkValidFormat(settings, field);
checkValue(field);
checkValue(settings.allowUnknownEnumValues, field);
}

if (beginString != null) {
Expand Down Expand Up @@ -788,7 +788,10 @@ private void checkValidFormat(ValidationSettings settings, StringField field) th
}
}

private void checkValue(StringField field) throws IncorrectTagValue {
private void checkValue(boolean allowUnknownEnumValues, StringField field) throws IncorrectTagValue {
if (allowUnknownEnumValues) {
return;
}
int tag = field.getField();
if (hasFieldValue(tag) && !isFieldValue(tag, field.getValue())) {
throw new IncorrectTagValue(tag);
Expand Down
17 changes: 17 additions & 0 deletions quickfixj-base/src/main/java/quickfix/ValidationSettings.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class ValidationSettings {
boolean checkUserDefinedFields = true;
boolean checkUnorderedGroupFields = true;
boolean allowUnknownMessageFields = false;
boolean allowUnknownEnumValues = false;
boolean firstFieldInGroupIsDelimiter = false;

public ValidationSettings() {}
Expand All @@ -35,6 +36,7 @@ public ValidationSettings(ValidationSettings validationSettings) {
this.checkUserDefinedFields = validationSettings.checkUserDefinedFields;
this.checkUnorderedGroupFields = validationSettings.checkUnorderedGroupFields;
this.allowUnknownMessageFields = validationSettings.allowUnknownMessageFields;
this.allowUnknownEnumValues = validationSettings.allowUnknownEnumValues;
this.firstFieldInGroupIsDelimiter = validationSettings.firstFieldInGroupIsDelimiter;
}

Expand Down Expand Up @@ -102,6 +104,21 @@ public void setAllowUnknownMessageFields(boolean allowUnknownFields) {
allowUnknownMessageFields = allowUnknownFields;
}

public boolean isAllowUnknownEnumValues() {
return allowUnknownEnumValues;
}

/**
* Controls whether unknown enumeration values are allowed,
* i.e. whether the validation of field values against the
* enumerated values defined in the data dictionary is skipped.
*
* @param allowUnknownEnumValues true = allowed (not validated), false = validated
*/
public void setAllowUnknownEnumValues(boolean allowUnknownEnumValues) {
this.allowUnknownEnumValues = allowUnknownEnumValues;
}

/**
* Controls whether any field which is
* first in the repeating group would be used as delimiter
Expand Down
48 changes: 48 additions & 0 deletions quickfixj-base/src/test/java/quickfix/DataDictionaryTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

Expand Down Expand Up @@ -669,6 +670,53 @@ public void testValidateFieldsOutOfOrderForGroups() throws Exception {
dictionary.validate(messageWithGroupLevel2, validationSettings);
}

@Test
public void testUnknownEnumValueIsRejectedByDefault() throws Exception {
final DataDictionary dictionary = new DataDictionary(getDictionary());
final ValidationSettings validationSettings = new ValidationSettings();
// TimeInForce(59)=Z is not a valid enum value in FIX 4.4
Message message = new Message(
"8=FIX.4.4\0019=136\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=Z\00110=239\001",
dictionary, validationSettings, false);

assertThrows(IncorrectTagValue.class, () -> dictionary.validate(message, validationSettings));

// multiple-value field ExecInst(18) with one invalid value (@)
Message messageWithMultipleValueField = new Message(
"8=FIX.4.4\0019=145\00135=D\00134=25\00149=SENDER\00156=TARGET\00152=20110412-13:43:00\001" +
"60=20110412-13:43:00\0011=testAccount\00111=123\00118=A @ D\00121=3\00138=42\00140=2\00144=42.37\001" +
"54=1\00155=QFJ\00159=0\00110=113\001",
dictionary, validationSettings, false);

assertThrows(IncorrectTagValue.class, () -> dictionary.validate(messageWithMultipleValueField, validationSettings));
}

@Test
public void testUnknownEnumValueIsAcceptedWhenAllowUnknownEnumValuesIsSet() throws Exception {
final DataDictionary dictionary = new DataDictionary(getDictionary());
final ValidationSettings validationSettings = new ValidationSettings();
validationSettings.setAllowUnknownEnumValues(true);
// TimeInForce(59)=Z is not a valid enum value in FIX 4.4
Message message = new Message(
"8=FIX.4.4\0019=136\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=Z\00110=239\001",
dictionary, validationSettings, false);

dictionary.validate(message, validationSettings);

// multiple-value field ExecInst(18) with one invalid value (@)
Message messageWithMultipleValueField = new Message(
"8=FIX.4.4\0019=145\00135=D\00134=25\00149=SENDER\00156=TARGET\00152=20110412-13:43:00\001" +
"60=20110412-13:43:00\0011=testAccount\00111=123\00118=A @ D\00121=3\00138=42\00140=2\00144=42.37\001" +
"54=1\00155=QFJ\00159=0\00110=113\001",
dictionary, validationSettings, false);

dictionary.validate(messageWithMultipleValueField, validationSettings);
}

@Test
public void shouldLoadDictionaryWhenExternalDTDisEnabled() throws ConfigError {
ExternalDtdDataDictionaryLoader.load("FIX_External_DTD.xml");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public void copyConstructor_retains_settings() {
final ValidationSettings validationSettings = new ValidationSettings();

validationSettings.setAllowUnknownMessageFields(true);
validationSettings.setAllowUnknownEnumValues(true);
validationSettings.setCheckFieldsHaveValues(false);
validationSettings.setCheckFieldsOutOfOrder(false);
validationSettings.setCheckUnorderedGroupFields(false);
Expand All @@ -19,6 +20,7 @@ public void copyConstructor_retains_settings() {
ValidationSettings validationSettingsCopy = new ValidationSettings(validationSettings);

assertEquals(validationSettingsCopy.isAllowUnknownMessageFields(), validationSettings.isAllowUnknownMessageFields());
assertEquals(validationSettingsCopy.isAllowUnknownEnumValues(), validationSettings.isAllowUnknownEnumValues());
assertEquals(validationSettingsCopy.isCheckFieldsHaveValues(), validationSettings.isCheckFieldsHaveValues());
assertEquals(validationSettingsCopy.isCheckFieldsOutOfOrder(), validationSettings.isCheckFieldsOutOfOrder());
assertEquals(validationSettingsCopy.isCheckUnorderedGroupFields(), validationSettings.isCheckUnorderedGroupFields());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ with QuickFIX, followed by an example.
| `ValidateSequenceNumbers` | Check the next expected target `SeqNum` against the received `SeqNum`. If enabled and a mismatch is detected: if lower than expected, logout; if higher, send a resend request. If not enabled and a mismatch is detected, nothing is done. Must be enabled for `EnableNextExpectedMsgSeqNum` to work. | `Y`<br/>`N` | `Y` |
| `ValidateChecksum` | If set to `N`, checksum validation will not be executed on messages. This setting cannot be set to `N` together with `RejectGarbledMessage` set to `Y`; in that case a `ConfigError` will be thrown. | `Y`<br/>`N` | `Y` |
| `AllowUnknownMsgFields` | If set to `Y`, non user defined fields (field with tag < 5000) will not be rejected if they are not defined in the data dictionary, or are present in messages they do not belong to. | `Y`<br/>`N` | `N` |
| `AllowUnknownEnumValues` | If set to `Y`, field values are not validated against the enumerated values defined in the data dictionary, i.e. messages containing unknown enum values are not rejected. Values of fields that have no enumerated values defined are not affected. To allow unknown values only for individual fields, use the `allowOtherValues` attribute on the field definition in the data dictionary instead. | `Y`<br/>`N` | `N` |
| `CheckCompID` | If set to `Y`, messages must be received from the counterparty with the correct `SenderCompID` and `TargetCompID`. Some systems will send you different CompIDs by design, so you must set this to `N`. | `Y`<br/>`N` | `Y` |
| `CheckLatency` | If set to `Y`, messages must be received from the counterparty within a defined number of seconds (see `MaxLatency`). It is useful to turn this off if a system uses local time for its timestamps instead of GMT. | `Y`<br/>`N` | `Y` |
| `MaxLatency` | If `CheckLatency` is set to `Y`, this defines the number of seconds latency allowed for a message to be processed. | positive integer | `120` |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,9 @@ private ValidationSettings createValidationSettings(SessionID sessionID, Session
validationSettings.setAllowUnknownMessageFields(settings.getBoolOrDefault(sessionID,
Session.SETTING_ALLOW_UNKNOWN_MSG_FIELDS, validationSettings.isAllowUnknownMessageFields()));

validationSettings.setAllowUnknownEnumValues(settings.getBoolOrDefault(sessionID,
Session.SETTING_ALLOW_UNKNOWN_ENUM_VALUES, validationSettings.isAllowUnknownEnumValues()));

validationSettings.setFirstFieldInGroupIsDelimiter(settings.getBoolOrDefault(sessionID,
Session.SETTING_FIRST_FIELD_IN_GROUP_IS_DELIMITER, validationSettings.isFirstFieldInGroupIsDelimiter()));

Expand Down
8 changes: 8 additions & 0 deletions quickfixj-core/src/main/java/quickfix/Session.java
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,14 @@ public class Session implements Closeable {
*/
public static final String SETTING_ALLOW_UNKNOWN_MSG_FIELDS = "AllowUnknownMsgFields";

/**
* Allow field values that are not defined as enumerated values in the data
* dictionary, i.e. skip the validation of field values against the enumerated
* values defined for the field. Values of fields without enumerated values
* are not affected.
*/
public static final String SETTING_ALLOW_UNKNOWN_ENUM_VALUES = "AllowUnknownEnumValues";

public static final String SETTING_DEFAULT_APPL_VER_ID = "DefaultApplVerID";

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,21 @@ public void testMinimalSettings() throws Exception {
factory.create(sessionID, settings);
}

@Test
public void testAllowUnknownEnumValuesSetting() throws Exception {
settings.setString(sessionID, Session.SETTING_ALLOW_UNKNOWN_ENUM_VALUES, "Y");
try (Session session = factory.create(sessionID, settings)) {
assertTrue(session.getValidationSettings().isAllowUnknownEnumValues());
}
}

@Test
public void testAllowUnknownEnumValuesDefaultsToFalse() throws Exception {
try (Session session = factory.create(sessionID, settings)) {
assertFalse(session.getValidationSettings().isAllowUnknownEnumValues());
}
}

@Test
public void testFixTMinimalSettings() throws Exception {
sessionID = new SessionID(FixVersions.BEGINSTRING_FIXT11, "SENDER", "TARGET");
Expand Down
Loading