Index: core/src/main/java/quickfix/Session.java =================================================================== --- core/src/main/java/quickfix/Session.java (revision 1047) +++ core/src/main/java/quickfix/Session.java (working copy) @@ -186,7 +186,7 @@ * validated. Values are "Y" or "N". Default is "Y". */ public static final String SETTING_VALIDATE_UNORDERED_GROUP_FIELDS = "ValidateUnorderedGroupFields"; - + /** * Session validation setting for enabling whether field values are * validated. Empty fields values are not allowed. Values are "Y" or "N". @@ -301,13 +301,13 @@ * Allow to disable heart beat failure detection */ public static final String SETTING_DISABLE_HEART_BEAT_CHECK = "DisableHeartBeatCheck"; - + /** * Return the last msg seq number processed (optional tag 369). Valid values are "Y" or "N". * Default is "N". */ public static final String SETTING_ENABLE_LAST_MSG_SEQ_NUM_PROCESSED = "EnableLastMsgSeqNumProcessed"; - + /** * Return the last msg seq number processed (optional tag 789). Valid values are "Y" or "N". @@ -419,7 +419,7 @@ boolean useClosedRangeForResend, double testRequestDelayMultiplier, DefaultApplVerID senderDefaultApplVerID, boolean validateSequenceNumbers, int[] logonIntervals, boolean resetOnError, boolean disconnectOnError, boolean ignoreHeartBeatFailure, - boolean rejectInvalidMessage, + boolean rejectInvalidMessage, boolean forceResendWhenCorruptedStore, Set allowedRemoteAddresses, boolean validateIncomingMessage, int resendRequestChunkSize, boolean enableNextExpectedMsgSeqNum, boolean enableLastMsgSeqNumProcessed) { this.application = application; @@ -452,7 +452,7 @@ this.resendRequestChunkSize = resendRequestChunkSize; this.enableNextExpectedMsgSeqNum = enableNextExpectedMsgSeqNum; this.enableLastMsgSeqNumProcessed = enableLastMsgSeqNumProcessed; - + final Log engineLog = logFactory.create(sessionID); if (engineLog instanceof SessionStateListener) { addStateListener((SessionStateListener) engineLog); @@ -647,7 +647,7 @@ } catch (IOException e) { log.error("Failed to close session resources", e); } - } + } } } } @@ -888,12 +888,14 @@ final Header header = message.getHeader(); final String msgType = header.getString(MsgType.FIELD); + final String sessionVersion = sessionID.getBeginString(); try { final String beginString = header.getString(BeginString.FIELD); - if (!beginString.equals(sessionID.getBeginString())) { - throw new UnsupportedVersion(); + if (!beginString.equals(sessionVersion)) { + throw new UnsupportedVersion( "Message version '" + beginString + + "' does not match the session version '" + sessionVersion + "'"); } if (msgType.equals(MsgType.LOGON)) { @@ -990,7 +992,7 @@ if (resetOrDisconnectIfRequired(message)) { return; } - if (sessionID.getBeginString().compareTo(FixVersions.BEGINSTRING_FIX42) >= 0 + if (sessionVersion.compareTo(FixVersions.BEGINSTRING_FIX42) >= 0 && message.isApp()) { generateBusinessReject(message, BusinessRejectReason.CONDITIONALLY_REQUIRED_FIELD_MISSING, e.field); @@ -1029,7 +1031,7 @@ if (resetOrDisconnectIfRequired(message)) { return; } - if (sessionID.getBeginString().compareTo(FixVersions.BEGINSTRING_FIX42) >= 0) { + if (sessionVersion.compareTo(FixVersions.BEGINSTRING_FIX42) >= 0) { generateBusinessReject(message, BusinessRejectReason.UNSUPPORTED_MESSAGE_TYPE, 0); } else { generateReject(message, "Unsupported message type"); @@ -1132,7 +1134,7 @@ */ private void manageGapFill(Message messageOutSync, int beginSeqNo, int endSeqNo) throws FieldNotFound, IOException, InvalidMessage { - + // Adjust the ending sequence number for older versions of FIX final String beginString = sessionID.getBeginString(); final int expectedSenderNum = getExpectedSenderNum(); @@ -1153,7 +1155,7 @@ } else { final ArrayList messages = new ArrayList(); - + try { state.get(beginSeqNo, endSeqNo, messages); } catch (final IOException e) { @@ -1170,21 +1172,21 @@ throw e; } } - + int msgSeqNum = 0; int begin = 0; int current = beginSeqNo; - + for (final String message : messages) { final Message msg = parseMessage(message); msgSeqNum = msg.getHeader().getInt(MsgSeqNum.FIELD); - + if ((current != msgSeqNum) && begin == 0) { begin = current; } - + final String msgType = msg.getHeader().getString(MsgType.FIELD); - + if (MessageUtils.isAdminMessage(msgType) && !forceResendWhenCorruptedStore) { if (begin == 0) { begin = msgSeqNum; @@ -1206,11 +1208,11 @@ } current = msgSeqNum + 1; } - + if (begin != 0) { generateSequenceReset(messageOutSync, begin, msgSeqNum + 1); } - + if (endSeqNo > msgSeqNum) { endSeqNo = endSeqNo + 1; final int next = state.getNextSenderMsgSeqNum(); @@ -1239,8 +1241,8 @@ } /** - * - * @param receivedMessage if not null, it is the message received and upon which the resend request is generated + * + * @param receivedMessage if not null, it is the message received and upon which the resend request is generated * @param beginSeqNo * @param endSeqNo * @throws FieldNotFound @@ -1327,11 +1329,11 @@ private void generateLogout(Message otherLogout) { generateLogout(otherLogout, null); } - + private void generateLogout(String reason) { generateLogout(null, reason); } - + /** * To generate a logout message * @param otherLogout if not null, the logout message that is causing a logout to be sent @@ -1382,7 +1384,7 @@ sendResendRequest(beginString, range[1] + 1, newSequence+1, range[1]); } } else if (newSequence < getExpectedTargetNum()) { - + getLog().onErrorEvent( "Invalid SequenceReset: newSequence=" + newSequence + " < expected=" + getExpectedTargetNum()); @@ -1611,12 +1613,12 @@ } else if (checkTooLow && isTargetTooLow(msgSeqNum) ) { doTargetTooLow(msg); return false; - } + } if (Logon.MSGTYPE.equals(msgType) && checkTooLow && isOtherSideTooHigh(msg) ) { doOtherSideTooHigh(msg); return false; } - + // Handle poss dup where msgSeq is as expected // FIX 4.4 Vol 2, test case 2f&g if (isPossibleDuplicate(msg) && !validatePossDup(msg)) { @@ -1632,7 +1634,7 @@ "ResendRequest for messages FROM " + range[0] + " TO " + range[1] + " has been satisfied."); state.setResendRange(0, 0, 0); - } + } } if (msgSeqNum < range[1] && range[2] > 0 && msgSeqNum >= range[2]) { String beginString = header.getString(BeginString.FIELD); @@ -1670,10 +1672,10 @@ } return validatePossDup(msg); } - + private boolean doOtherSideTooHigh(Message msg) throws FieldNotFound, IOException { if (!isPossibleDuplicate(msg)) { - + final String text = "Tag 789 (NextExpectedMsgSeqNum) is higher than expected. Expected " + getExpectedTargetNum() + ", Received " + msg.getInt(NextExpectedMsgSeqNum.FIELD); generateLogout(text); @@ -1891,9 +1893,10 @@ /** * Use disconnect(reason, logError) instead. - * - * @deprecated + * + * @deprecated */ + @Deprecated public void disconnect() throws IOException { disconnect("Other reason", true); } @@ -2033,7 +2036,7 @@ manageGapFill(logon, beginSeqNo, endSeqNo); } catch (Exception e) { getLog().onErrorEvent("Synchronization on logon message is failed"); - } + } } } if (isLoggedOn()) { @@ -2128,11 +2131,11 @@ private void sendResendRequest(String beginString, int msgSeqNum, int beginSeqNo, int endSeqNo) { - + int lastEndSeqNoSent = resendRequestChunkSize ==0 ? endSeqNo : beginSeqNo + resendRequestChunkSize - 1; if (lastEndSeqNoSent > endSeqNo) { lastEndSeqNoSent = endSeqNo; - } + } if (lastEndSeqNoSent == endSeqNo && !useClosedRangeForResend) { if (beginString.compareTo("FIX.4.2") >= 0) { endSeqNo = 0; @@ -2142,7 +2145,7 @@ } else { endSeqNo = lastEndSeqNoSent; } - + Message resendRequest = messageFactory.create(beginString, MsgType.RESEND_REQUEST); resendRequest.setInt(BeginSeqNo.FIELD, beginSeqNo); resendRequest.setInt(EndSeqNo.FIELD, endSeqNo); @@ -2150,7 +2153,7 @@ sendRaw(resendRequest, 0); getLog().onEvent("Sent ResendRequest FROM: " + beginSeqNo + " TO: " + lastEndSeqNoSent); state.setResendRange(beginSeqNo, msgSeqNum - 1, resendRequestChunkSize == 0 ? 0 : lastEndSeqNoSent); - + } private boolean validatePossDup(Message msg) throws FieldNotFound, IOException { @@ -2205,7 +2208,7 @@ /** * Send the message * @param message is the message to send - * @param num is the seq num of the message to send, if 0, + * @param num is the seq num of the message to send, if 0, * @return */ private boolean sendRaw(Message message, int num) { @@ -2223,7 +2226,7 @@ if (num > 0) { header.setInt(MsgSeqNum.FIELD, num); } - + if (enableLastMsgSeqNumProcessed) { if (!header.isSetField(LastMsgSeqNumProcessed.FIELD)) header.setInt(LastMsgSeqNumProcessed.FIELD, getExpectedTargetNum() - 1); } @@ -2342,6 +2345,7 @@ * @deprecated * @param dataDictionary */ + @Deprecated public void setDataDictionary(DataDictionary dataDictionary) { throw new UnsupportedOperationException( "Modification of session dictionary is not supported in QFJ"); @@ -2493,6 +2497,7 @@ return state.getTestRequestDelayMultiplier(); } + @Override public String toString() { String s = sessionID.toString(); try { @@ -2586,7 +2591,7 @@ } /** - * Closes session resources. This is for internal use and should typically + * Closes session resources. This is for internal use and should typically * not be called by an user application. */ public void close() throws IOException { @@ -2599,4 +2604,4 @@ ((Closeable)resource).close(); } } -} \ No newline at end of file +} Index: core/src/main/java/quickfix/UnsupportedVersion.java =================================================================== --- core/src/main/java/quickfix/UnsupportedVersion.java (revision 1047) +++ core/src/main/java/quickfix/UnsupportedVersion.java (working copy) @@ -1,19 +1,19 @@ /******************************************************************************* - * 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 + * 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. ******************************************************************************/ @@ -21,4 +21,9 @@ class UnsupportedVersion extends RuntimeException { -} \ No newline at end of file + public UnsupportedVersion(final String message) { + + super( message ); + } + +} Index: core/src/test/java/quickfix/DataDictionaryTest.java =================================================================== --- core/src/test/java/quickfix/DataDictionaryTest.java (revision 1047) +++ core/src/test/java/quickfix/DataDictionaryTest.java (working copy) @@ -246,7 +246,8 @@ newSingle.setField(new Account("testAccount")); final DataDictionary dd = getDictionary(); - new ExpectedTestFailure(UnsupportedVersion.class, null) { + new ExpectedTestFailure(UnsupportedVersion.class, + "Message version 'FIX.4.3' does not match the data dictionary version 'FIX.4.4'") { @Override protected void execute() throws Throwable { dd.validate(newSingle); @@ -255,7 +256,7 @@ // 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); } // QF C++ treats the string argument as a filename although it's Index: core/src/main/java/quickfix/DataDictionary.java =================================================================== --- core/src/main/java/quickfix/DataDictionary.java (revision 1047) +++ core/src/main/java/quickfix/DataDictionary.java (working copy) @@ -541,7 +541,7 @@ gi.getDataDictionary().setAllowUnknownMessageFields(allowUnknownFields); } } - + private void copyFrom(DataDictionary rhs) { hasVersion = rhs.hasVersion; beginString = rhs.beginString; @@ -629,12 +629,12 @@ IncorrectDataFormat { final boolean bodyOnly = sessionDataDictionary == null; - if (isVersionSpecified(sessionDataDictionary) - && !sessionDataDictionary.getVersion().equals( - message.getHeader().getString(BeginString.FIELD)) - && !message.getHeader().getString(BeginString.FIELD).equals("FIXT.1.1") - && !sessionDataDictionary.getVersion().equals("FIX.5.0")) { - throw new UnsupportedVersion(); + final String dictionaryVersion = sessionDataDictionary.getVersion(); + final String messageVersion = message.getHeader().getString(BeginString.FIELD); + if (isVersionSpecified(sessionDataDictionary) && !dictionaryVersion.equals(messageVersion) + && !messageVersion.equals("FIXT.1.1") && !dictionaryVersion.equals("FIX.5.0")) { + throw new UnsupportedVersion("Message version '" + messageVersion + + "' does not match the data dictionary version '" + dictionaryVersion + "'"); } if (!message.hasValidStructure() && message.getException() != null) { @@ -1264,6 +1264,7 @@ // return stringValue; //} + @Override public boolean equals(Object other) { if (this == other) { return true; @@ -1275,6 +1276,7 @@ && stringValue.equals(((IntStringPair) other).stringValue); } + @Override public int hashCode() { return stringValue.hashCode() + intValue; } @@ -1282,6 +1284,7 @@ /** * For debugging */ + @Override public String toString() { final StringBuffer b = new StringBuffer(); b.append('(').append(intValue).append(',').append(stringValue).append(')'); @@ -1315,6 +1318,7 @@ return delimeterField; } + @Override public boolean equals(Object other) { if (this == other) { return true; @@ -1326,6 +1330,7 @@ && dataDictionary.equals(((GroupInfo) other).dataDictionary); } + @Override public int hashCode() { return delimeterField; }