/******************************************************************************* * 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; import java.util.HashMap; import java.util.Map; import org.quickfixj.QFJException; import quickfix.Message.Header; import quickfix.field.ApplVerID; import quickfix.field.BeginString; import quickfix.field.DefaultApplVerID; import quickfix.field.MsgType; import quickfix.field.SenderCompID; import quickfix.field.SenderLocationID; import quickfix.field.SenderSubID; import quickfix.field.TargetCompID; import quickfix.field.TargetLocationID; import quickfix.field.TargetSubID; public class MessageUtils { private static final char FIELD_SEPARATOR = '\001'; public static SessionID getSessionID(Message fixMessage) { final Header header = fixMessage.getHeader(); return new SessionID(getFieldOrDefault(header, BeginString.FIELD, null), getFieldOrDefault( header, SenderCompID.FIELD, null), getFieldOrDefault(header, SenderSubID.FIELD, null), getFieldOrDefault(header, SenderLocationID.FIELD, null), getFieldOrDefault( header, TargetCompID.FIELD, null), getFieldOrDefault(header, TargetSubID.FIELD, null), getFieldOrDefault(header, TargetLocationID.FIELD, null), null); } public static SessionID getSessionID(String messageString) { return new SessionID(getStringField(messageString, BeginString.FIELD), getStringField( messageString, SenderCompID.FIELD), getStringField(messageString, SenderSubID.FIELD), getStringField(messageString, SenderLocationID.FIELD), getStringField(messageString, TargetCompID.FIELD), getStringField(messageString, TargetSubID.FIELD), getStringField(messageString, TargetLocationID.FIELD), null); } public static SessionID getReverseSessionID(Message fixMessage) { final Header header = fixMessage.getHeader(); return new SessionID(getFieldOrDefault(header, BeginString.FIELD, null), getFieldOrDefault( header, TargetCompID.FIELD, null), getFieldOrDefault(header, TargetSubID.FIELD, null), getFieldOrDefault(header, TargetLocationID.FIELD, null), getFieldOrDefault( header, SenderCompID.FIELD, null), getFieldOrDefault(header, SenderSubID.FIELD, null), getFieldOrDefault(header, SenderLocationID.FIELD, null), null); } public static SessionID getReverseSessionID(String messageString) { return new SessionID(getStringField(messageString, BeginString.FIELD), getStringField( messageString, TargetCompID.FIELD), getStringField(messageString, TargetSubID.FIELD), getStringField(messageString, TargetLocationID.FIELD), getStringField(messageString, SenderCompID.FIELD), getStringField(messageString, SenderSubID.FIELD), getStringField(messageString, SenderLocationID.FIELD), null); } private static String getFieldOrDefault(FieldMap fields, int tag, String defaultValue) { if (fields.isSetField(tag)) { try { return fields.getString(tag); } catch (final FieldNotFound e) { // ignore, should never happen return null; } } else { return defaultValue; } } /** * Utility method for parsing a mesasge. This should only be used for parsing messages from * FIX versions 4.4 or earlier. * * @param messageFactory * @param dataDictionary * @param messageString * @return the parsed message * @throws InvalidMessage */ public static Message parse(MessageFactory messageFactory, DataDictionary dataDictionary, String messageString) throws InvalidMessage { final int index = messageString.indexOf(FIELD_SEPARATOR); if (index < 0) { throw new InvalidMessage("Message does not contain any field separator"); } 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); return message; } /** * NOTE: This method is intended for internal use. * * @param session - the Session that will process the message * @param messageString * @return the parsed message * @throws InvalidMessage */ public static Message parse(Session session, String messageString) throws InvalidMessage { final String beginString = getStringField(messageString, BeginString.FIELD); final String msgType = getMessageType(messageString); ApplVerID applVerID = null; if (FixVersions.BEGINSTRING_FIXT11.equals(beginString)) { applVerID = getApplVerID(session, messageString); } else { applVerID = toApplVerID(beginString); } final MessageFactory messageFactory = session.getMessageFactory(); final DataDictionaryProvider ddProvider = session.getDataDictionaryProvider(); final DataDictionary sessionDataDictionary = ddProvider == null ? null : ddProvider .getSessionDataDictionary(beginString); final DataDictionary applicationDataDictionary = ddProvider == null ? null : ddProvider .getApplicationDataDictionary(applVerID); final quickfix.Message message = messageFactory.create(beginString, msgType); final DataDictionary payloadDictionary = MessageUtils.isAdminMessage(msgType) ? sessionDataDictionary : applicationDataDictionary; message.parse(messageString, sessionDataDictionary, payloadDictionary, payloadDictionary != null); return message; } private static ApplVerID getApplVerID(Session session, String messageString) throws InvalidMessage { ApplVerID applVerID = null; final String applVerIdString = getStringField(messageString, ApplVerID.FIELD); if (applVerIdString != null) { applVerID = new ApplVerID(applVerIdString); } if (applVerID == null) { applVerID = session.getTargetDefaultApplicationVersionID(); } if (applVerID == null && isLogon(messageString)) { final String defaultApplVerIdString = getStringField(messageString, DefaultApplVerID.FIELD); if (defaultApplVerIdString != null) { applVerID = new ApplVerID(defaultApplVerIdString); } } if (applVerID == null) { throw new InvalidMessage("Can't determine ApplVerID for message"); } return applVerID; } public static boolean isAdminMessage(String msgType) { return msgType.length() == 1 && "0A12345".indexOf(msgType) != -1; } public static boolean isHeartbeat(String message) { return isMessageType(message, MsgType.HEARTBEAT); } public static boolean isLogon(String message) { return isMessageType(message, MsgType.LOGON); } private static boolean isMessageType(String message, String msgType) { try { return msgType.equals(getMessageType(message)); } catch (final InvalidMessage e) { return false; } } public static String getMessageType(String messageString) throws InvalidMessage { final String value = getStringField(messageString, 35); if (value == null) { throw new InvalidMessage("Missing or garbled message type in " + messageString); } return value; } public static String getStringField(String messageString, int tag) { String value = null; final String tagString = Integer.toString(tag); int start = messageString.indexOf(tagString, 0); while (start != -1 && value == null) { if ((start == 0 || messageString.charAt(start - 1) == FIELD_SEPARATOR)) { int end = start + tagString.length(); if ((end + 1) < messageString.length() && messageString.charAt(end) == '=') { // found tag, get value start = end = (end + 1); for (; end < messageString.length() && messageString.charAt(end) != FIELD_SEPARATOR; end++) { ; } if (end == messageString.length()) { return null; } else { value = messageString.substring(start, end); } } } start = messageString.indexOf(tagString, start + 1); } return value; } private static Map<String, String> applVerIDtoBeginString = new HashMap<String, String>() { { // No support for earlier versions of FIX put(ApplVerID.FIX40, FixVersions.BEGINSTRING_FIX40); put(ApplVerID.FIX41, FixVersions.BEGINSTRING_FIX41); put(ApplVerID.FIX42, FixVersions.BEGINSTRING_FIX42); put(ApplVerID.FIX43, FixVersions.BEGINSTRING_FIX43); put(ApplVerID.FIX44, FixVersions.BEGINSTRING_FIX44); put(ApplVerID.FIX50, FixVersions.FIX50); put(ApplVerID.FIX50SP1, FixVersions.FIX50SP1); put(ApplVerID.FIX50SP2, FixVersions.FIX50SP2); } }; /** * Convert an ApplVerID to a "begin string" * @param applVerID * @return the begin string for the specified ApplVerID. * @throws QFJException if conversion fails. * @see ApplVerID */ public static String toBeginString(ApplVerID applVerID) throws QFJException { final String beginString = applVerIDtoBeginString.get(applVerID.getValue()); if (beginString == null) { throw new QFJException("Unknown or unsupported ApplVerID: " + applVerID.getValue()); } return beginString; } private static Map<String, ApplVerID> beginStringToApplVerID = new HashMap<String, ApplVerID>() { { // No support for earlier versions of FIX put(FixVersions.BEGINSTRING_FIX40, new ApplVerID(ApplVerID.FIX40)); put(FixVersions.BEGINSTRING_FIX41, new ApplVerID(ApplVerID.FIX41)); put(FixVersions.BEGINSTRING_FIX42, new ApplVerID(ApplVerID.FIX42)); put(FixVersions.BEGINSTRING_FIX43, new ApplVerID(ApplVerID.FIX43)); put(FixVersions.BEGINSTRING_FIX44, new ApplVerID(ApplVerID.FIX44)); put(FixVersions.FIX50, new ApplVerID(ApplVerID.FIX50)); put(FixVersions.FIX50SP1, new ApplVerID(ApplVerID.FIX50SP1)); put(FixVersions.FIX50SP2, new ApplVerID(ApplVerID.FIX50SP2)); } }; /** * Convert a begin string to an ApplVerID * @param beginString * @return the ApplVerID for the specified begin string. * @throws QFJException if conversion fails. * @see FixVersions */ public static ApplVerID toApplVerID(String beginString) throws QFJException { final ApplVerID applVerID = beginStringToApplVerID.get(beginString); if (applVerID == null) { throw new QFJException("Can't convert to ApplVerID: " + beginString); } return applVerID; } }