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
4 changes: 3 additions & 1 deletion quickfixj-core/src/main/java/quickfix/FieldException.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

package quickfix;

public class FieldException extends RuntimeException {
public class FieldException extends RuntimeException implements HasFieldAndReason {

private final int field;

Expand All @@ -45,10 +45,12 @@ public boolean isFieldSpecified() {
return field != -1;
}

@Override
public int getField() {
return field;
}

@Override
public int getSessionRejectReason() {
return sessionRejectReason;
}
Expand Down
2 changes: 1 addition & 1 deletion quickfixj-core/src/main/java/quickfix/FieldNotFound.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
public class FieldNotFound extends Exception {

public FieldNotFound(int field) {
super("Field [" + field + "] was not found in message.");
super("Field was not found in message, field=" + field);
this.field = field;
}

Expand Down
27 changes: 27 additions & 0 deletions quickfixj-core/src/main/java/quickfix/HasFieldAndReason.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*******************************************************************************
* 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;

interface HasFieldAndReason {

int getField();
int getSessionRejectReason();
String getMessage();
}
26 changes: 22 additions & 4 deletions quickfixj-core/src/main/java/quickfix/IncorrectDataFormat.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,22 @@

package quickfix;

import quickfix.field.SessionRejectReason;

/**
* Field has a badly formatted value. (From the C++ API documentation.)
*/
public class IncorrectDataFormat extends Exception {
public final int field;
public final String data;
public class IncorrectDataFormat extends Exception implements HasFieldAndReason {
private final int field;
private final String data;
private final int sessionRejectReason;

/**
* @param field the tag number with the incorrect data
* @param data the incorrect data
*/
public IncorrectDataFormat(final int field, final String data) {
this(field, data, "Field [" + field + "] contains badly formatted data.");
this(field, data, SessionRejectReasonText.getMessage(SessionRejectReason.INCORRECT_DATA_FORMAT_FOR_VALUE) + ", field=" + field);
}

/**
Expand All @@ -51,10 +54,25 @@ public IncorrectDataFormat(final int field) {
public IncorrectDataFormat(final String message) {
this(0, null, message);
}

@Override
public int getSessionRejectReason() {
return sessionRejectReason;
}

@Override
public int getField() {
return field;
}

public String getData() {
return data;
}

private IncorrectDataFormat(final int field, final String data, final String message) {
super(message);
this.field = field;
this.data = data;
this.sessionRejectReason = SessionRejectReason.INCORRECT_DATA_FORMAT_FOR_VALUE;
}
}
27 changes: 19 additions & 8 deletions quickfixj-core/src/main/java/quickfix/IncorrectTagValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,28 @@

package quickfix;

import quickfix.field.SessionRejectReason;

/**
* An exception thrown when a tags value is not valid according to the data dictionary.
*/
public class IncorrectTagValue extends Exception {
public class IncorrectTagValue extends Exception implements HasFieldAndReason {

private String value;
private final int field;
private final int sessionRejectReason;

public IncorrectTagValue(int field) {
super("Field [" + field + "] contains an incorrect tag value.");
super(SessionRejectReasonText.getMessage(SessionRejectReason.VALUE_IS_INCORRECT) + ", field=" + field);
this.field = field;
this.sessionRejectReason = SessionRejectReason.VALUE_IS_INCORRECT;
}

public IncorrectTagValue(int field, String value) {
super();
this.field = field;
this.value = value;
}

public IncorrectTagValue(String s) {
super(s);
this.sessionRejectReason = SessionRejectReason.VALUE_IS_INCORRECT;
}

@Override
Expand All @@ -49,7 +53,14 @@ public String toString() {
return str;
}

public int field;
@Override
public int getField() {
return field;
}

public String value;
@Override
public int getSessionRejectReason() {
return sessionRejectReason;
}

}
50 changes: 28 additions & 22 deletions quickfixj-core/src/main/java/quickfix/Session.java
Original file line number Diff line number Diff line change
Expand Up @@ -1015,20 +1015,11 @@ private void next(Message message, boolean isProcessingQueuedMessages) throws Fi
state.incrNextTargetMsgSeqNum();
break;
}
} catch (final FieldException e) {
} catch (final FieldException | IncorrectDataFormat | IncorrectTagValue e) {
if (logErrorAndDisconnectIfRequired(e, message)) {
return;
}
if (msgType.equals(MsgType.LOGON)) {
final String reason = SessionRejectReasonText.getMessage(e.getSessionRejectReason());
final String errorMessage = "Invalid Logon message: " + (reason != null ? reason : "unspecific reason")
+ " (field " + e.getField() + ")";
generateLogout(errorMessage);
state.incrNextTargetMsgSeqNum();
disconnect(errorMessage, true);
} else {
generateReject(message, e.getMessage(), e.getSessionRejectReason(), e.getField());
}
handleExceptionAndRejectMessage(msgType, message, e);
} catch (final FieldNotFound e) {
if (logErrorAndDisconnectIfRequired(e, message)) {
return;
Expand All @@ -1045,15 +1036,11 @@ private void next(Message message, boolean isProcessingQueuedMessages) throws Fi
generateReject(message, SessionRejectReason.REQUIRED_TAG_MISSING, e.field);
}
}
} catch (final IncorrectDataFormat e) {
if (logErrorAndDisconnectIfRequired(e, message)) {
return;
}
generateReject(message, SessionRejectReason.INCORRECT_DATA_FORMAT_FOR_VALUE, e.field);
} catch (final IncorrectTagValue e) {
getLog().onErrorEvent("Rejecting invalid message: " + e + ": " + message);
generateReject(message, SessionRejectReason.VALUE_IS_INCORRECT, e.field);
} catch (final InvalidMessage e) {
/* InvalidMessage means a low-level error (e.g. checksum problem) and we should
ignore the message and let the problem correct itself (optimistic approach).
Target sequence number is not incremented, so it will trigger a ResendRequest
on the next message that is received. */
getLog().onErrorEvent("Skipping invalid message: " + e + ": " + message);
if (resetOrDisconnectIfRequired(message)) {
return;
Expand Down Expand Up @@ -1135,9 +1122,28 @@ private void next(Message message, boolean isProcessingQueuedMessages) throws Fi
}
}

private void handleExceptionAndRejectMessage(final String msgType, final Message message, final HasFieldAndReason e) throws FieldNotFound, IOException {
if (MsgType.LOGON.equals(msgType)) {
logoutWithErrorMessage(e.getMessage());
} else {
getLog().onErrorEvent("Rejecting invalid message: " + e + ": " + message);
generateReject(message, e.getMessage(), e.getSessionRejectReason(), e.getField());
}
}

private void logoutWithErrorMessage(final String reason) throws IOException {
final String errorMessage = "Invalid Logon message: " + (reason != null ? reason : "unspecific reason");
generateLogout(errorMessage);
state.incrNextTargetMsgSeqNum();
disconnect(errorMessage, true);
}

private boolean logErrorAndDisconnectIfRequired(final Exception e, Message message) {
getLog().onErrorEvent("Rejecting invalid message: " + e + ": " + message);
return resetOrDisconnectIfRequired(message);
final boolean resetOrDisconnectIfRequired = resetOrDisconnectIfRequired(message);
if (resetOrDisconnectIfRequired) {
getLog().onErrorEvent("Encountered invalid message: " + e + ": " + message);
}
return resetOrDisconnectIfRequired;
}

/**
Expand Down Expand Up @@ -1600,7 +1606,7 @@ private void setRejectReason(Message reject, int field, String reason,
} else {
String rejectReason = reason;
if (includeFieldInReason && !rejectReason.endsWith("" + field) ) {
rejectReason = rejectReason + " (" + field + ")";
rejectReason = rejectReason + ", field=" + field;
}
reject.setString(Text.FIELD, rejectReason);
}
Expand Down
7 changes: 3 additions & 4 deletions quickfixj-core/src/test/java/quickfix/ExceptionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,13 @@ public void testDoNotSend() {

public void testIncorrectDataFormat() {
IncorrectDataFormat e = new IncorrectDataFormat(5, "test");
assertEquals(5, e.field);
assertEquals("test", e.data);
assertEquals(5, e.getField());
assertEquals("test", e.getData());
}

public void testIncorrectTagValue() {
new IncorrectTagValue(5);
IncorrectTagValue e = new IncorrectTagValue("test");
e.field = 5;
IncorrectTagValue e = new IncorrectTagValue(5, "test");
}

public void testRejectLogon() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public void testInvokerException3() throws Exception {
MessageCracker cracker = new MessageCracker() {
@Handler
public void handle(quickfix.fixt11.Logon logon, SessionID sessionID) throws IncorrectTagValue {
throw new IncorrectTagValue("test");
throw new IncorrectTagValue(0, "test");
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,7 @@ public void testInvalidEnumFieldInGroup() throws Exception {
Assert.fail("No exception");
} catch (final IncorrectTagValue e) {
// expected
assertEquals(385, e.field);
assertEquals(385, e.getField());
}
}

Expand Down
27 changes: 21 additions & 6 deletions quickfixj-core/src/test/java/quickfix/SessionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -855,8 +855,8 @@ public void testSessionNotResetRightAfterLogonOnInitiator() throws Exception {
}

@Test
// QFJ-929
public void testLogonIsAnsweredWithLogoutOnFieldException() throws Exception {
// QFJ-929/QFJ-933
public void testLogonIsAnsweredWithLogoutOnException() throws Exception {

final SessionID sessionID = new SessionID(
FixVersions.BEGINSTRING_FIX44, "SENDER", "TARGET");
Expand Down Expand Up @@ -900,17 +900,32 @@ public void testLogonIsAnsweredWithLogoutOnFieldException() throws Exception {
session.setResponder(responder);
session.logon();
session.next();
setUpHeader(session.getSessionID(), logonRequest, true, 2);
logonRequest.removeField(HeartBtInt.FIELD);
setUpHeader(session.getSessionID(), logonRequest, true, 3);
logonRequest.setString(EncryptMethod.FIELD, "A");
logonRequest.toString(); // calculate length and checksum
session.next(logonRequest);
// session should not be logged on due to missing HeartBtInt
// session should not be logged on due to IncorrectDataFormat
assertFalse(session.isLoggedOn());

assertEquals(3, application.lastToAdminMessage().getHeader().getInt(MsgSeqNum.FIELD));
assertEquals(MsgType.LOGOUT, application.lastToAdminMessage().getHeader().getString(MsgType.FIELD));
assertEquals(MsgType.LOGOUT, application.lastToAdminMessage().getHeader().getString(MsgType.FIELD));
assertEquals(4, session.getStore().getNextTargetMsgSeqNum());
assertEquals(4, session.getStore().getNextSenderMsgSeqNum());

session.setResponder(responder);
session.logon();
session.next();
setUpHeader(session.getSessionID(), logonRequest, true, 3);
logonRequest.setString(EncryptMethod.FIELD, "99");
logonRequest.toString(); // calculate length and checksum
session.next(logonRequest);
// session should not be logged on due to IncorrectTagValue
assertFalse(session.isLoggedOn());

assertEquals(4, application.lastToAdminMessage().getHeader().getInt(MsgSeqNum.FIELD));
assertEquals(MsgType.LOGOUT, application.lastToAdminMessage().getHeader().getString(MsgType.FIELD));
assertEquals(5, session.getStore().getNextTargetMsgSeqNum());
assertEquals(5, session.getStore().getNextSenderMsgSeqNum());

session.close();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ E8=FIX.4.09=5635=A34=149=ISLD52=00000000-00:00:0056=TW98=0108=210=0
#New order message with incorrect enum value. Handling instructions (21) = 4
I8=FIX.4.035=D34=249=TW52=<TIME>56=ISLD11=ID21=440=154=138=002000.0055=INTC60=<TIME>
# expect a reject
E8=FIX.4.09=10535=334=249=ISLD52=00000000-00:00:0056=TW45=258=Value is incorrect (out of range) for this tag (21)10=0
E8=FIX.4.09=10535=334=249=ISLD52=00000000-00:00:0056=TW45=258=Value is incorrect (out of range) for this tag, field=2110=0

#------------------------
# end message exchange
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ E8=FIX.4.09=5635=A34=149=ISLD52=00000000-00:00:0056=TW98=0108=210=0
#New order message with incorrect value. Qty (38) has a leading + sign
I8=FIX.4.035=D34=249=TW52=<TIME>56=ISLD11=ID21=140=154=138=+200.0055=INTC60=<TIME>
# expect a reject
E8=FIX.4.09=9035=334=249=ISLD52=00000000-00:00:0056=TW45=258=Incorrect data format for value (38)10=0
E8=FIX.4.09=9035=334=249=ISLD52=00000000-00:00:0056=TW45=258=Incorrect data format for value, field=3810=0

#------------------------
# end message exchange
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ E8=FIX.4.09=8135=D34=349=ISLD52=00000000-00:00:0056=TW11=ID21=338=1004
I8=FIX.4.035=D34=249=TW52=<TIME>56=ISLD43=Y11=ID21=338=10040=154=155=INTC

# reject message
E8=FIX.4.09=8035=334=449=ISLD52=00000000-00:00:0056=TW45=258=Required tag missing (122)10=0
E8=FIX.4.09=8035=334=449=ISLD52=00000000-00:00:0056=TW45=258=Required tag missing, field=12210=0

# make sure sequence number no incremented
I8=FIX.4.035=134=449=TW52=<TIME>56=ISLD112=HELLO
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ E8=FIX.4.09=5935=234=249=ISLD52=00000000-00:00:0056=TW7=216=99999910=0
# send order with badly formatted field 126
I8=FIX.4.035=D34=243=Y49=TW52=<TIME>56=ISLD122=<TIME-10>11=ID21=338=10040=154=155=IVP126=20040415
# receive session reject
E8=FIX.4.09=9135=334=349=ISLD52=00000000-00:00:0056=TW45=258=Incorrect data format for value (126)10=245
E8=FIX.4.09=9135=334=349=ISLD52=00000000-00:00:0056=TW45=258=Incorrect data format for value, field=12610=245

# send in a second test request, tests requests should be processed in order
I8=FIX.4.035=134=449=TW52=<TIME>56=ISLD112=HELLO2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@ E8=FIX.4.09=5735=A34=149=ISLD52=00000000-00:00:0056=TW98=0108=3010=0
# Send an order with OnBehalfOfCompID (115) set with bad field value 40=w
I8=FIX.4.035=D34=249=TW52=<TIME>56=ISLD115=JCD11=ID21=338=10040=w54=155=INTC60=<TIME>
# Expect to see a reject with reverse route DeliverToCompID (128) set
E8=FIX.4.09=11335=334=249=ISLD52=00000000-00:00:0056=TW128=JCD45=258=Value is incorrect (out of range) for this tag (40)10=0
E8=FIX.4.09=11335=334=249=ISLD52=00000000-00:00:0056=TW128=JCD45=258=Value is incorrect (out of range) for this tag, field=4010=0

# Send an order with DeliverToCompID (128) set with bad field value 40=w
I8=FIX.4.035=D34=349=TW52=<TIME>56=ISLD128=JCD11=ID21=338=10040=w54=155=INTC60=<TIME>
# Expect to see a reject with reverse route OnBehalfOfCompID (115) set
E8=FIX.4.09=11335=334=349=ISLD52=00000000-00:00:0056=TW115=JCD45=358=Value is incorrect (out of range) for this tag (40)10=0
E8=FIX.4.09=11335=334=349=ISLD52=00000000-00:00:0056=TW115=JCD45=358=Value is incorrect (out of range) for this tag, field=4010=0

# Send an order with OnBehalfOfCompID (115) and OnBehalfOfSubID (116) set with bad field value 40=w
I8=FIX.4.035=D34=449=TW52=<TIME>56=ISLD115=JCD116=CS11=ID21=338=10040=w54=155=INTC60=<TIME>
# Expect to see a reject with reverse route DeliverToCompID (128) and DeliverToSubID (129) set
E8=FIX.4.09=12035=334=449=ISLD52=00000000-00:00:0056=TW128=JCD129=CS45=458=Value is incorrect (out of range) for this tag (40)10=0
E8=FIX.4.09=12035=334=449=ISLD52=00000000-00:00:0056=TW128=JCD129=CS45=458=Value is incorrect (out of range) for this tag, field=4010=0

# Send an order with DeliverToCompID (128) and DeliverToSubID (129) set with bad field value 40=w
I8=FIX.4.035=D34=549=TW52=<TIME>56=ISLD128=JCD129=CS11=ID21=338=10040=w54=155=INTC60=<TIME>
# Expect to see reverse route OnBehalfOfCompID (115) and OnBehalfOfSubID (116) set
E8=FIX.4.09=12035=334=549=ISLD52=00000000-00:00:0056=TW115=JCD116=CS45=558=Value is incorrect (out of range) for this tag (40)10=0
E8=FIX.4.09=12035=334=549=ISLD52=00000000-00:00:0056=TW115=JCD116=CS45=558=Value is incorrect (out of range) for this tag, field=4010=0

# Send an order with OnBehalfOfCompID (115) and OnBehalfOfSubID (116) and OnBehalfOfLocationID (144) set with bad field value 40=w
I8=FIX.4.035=D34=649=TW52=<TIME>56=ISLD115=JCD116=CS144=CHI11=ID21=338=10040=w54=155=INTC60=<TIME>
Expand Down
Loading