Skip to content

QFJ-888: DefaultMessageFactory does not support FIX5.0 SP1/2 #171

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 8, 2018
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
50 changes: 35 additions & 15 deletions quickfixj-core/src/main/java/quickfix/DefaultMessageFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@

package quickfix;

import quickfix.field.ApplVerID;
import quickfix.field.MsgType;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

import static quickfix.FixVersions.BEGINSTRING_FIX40;
Expand All @@ -40,15 +42,37 @@
public class DefaultMessageFactory implements MessageFactory {
private final Map<String, MessageFactory> messageFactories = new ConcurrentHashMap<>();

private final ApplVerID defaultApplVerID;

/**
* Constructs a DefaultMessageFactory, which dynamically loads and delegates to
* the default version-specific message factories, if they are available at runtime.
* <p>
* Callers can set the {@link Thread#setContextClassLoader context classloader},
* which will be used to load the classes if {@link Class#forName Class.forName}
* fails to do so (e.g. in an OSGi environment).
* <p>
* Equivalent to {@link #DefaultMessageFactory(String) DefaultMessageFactory}({@link ApplVerID#FIX50 ApplVerID.FIX50}).
*/
public DefaultMessageFactory() {
this(ApplVerID.FIX50);
}

/**
* Constructs a DefaultMessageFactory, which dynamically loads and delegates to
* the default version-specific message factories, if they are available at runtime.
* <p>
* Callers can set the {@link Thread#setContextClassLoader context classloader},
* which will be used to load the classes if {@link Class#forName Class.forName}
* fails to do so (e.g. in an OSGi environment).
*
* @param defaultApplVerID ApplVerID value used by default for {@link #create(String, ApplVerID, String)}
*/
public DefaultMessageFactory(String defaultApplVerID) {
Objects.requireNonNull(defaultApplVerID, "defaultApplVerID");

this.defaultApplVerID = new ApplVerID(defaultApplVerID);

// To loosen the coupling between this factory and generated code, the
// message factories are discovered at run time using reflection
addFactory(BEGINSTRING_FIX40);
Expand Down Expand Up @@ -118,27 +142,23 @@ public void addFactory(String beginString, Class<? extends MessageFactory> facto
}
}

@Override
public Message create(String beginString, String msgType) {
return create(beginString, defaultApplVerID, msgType);
}

@Override
public Message create(String beginString, ApplVerID applVerID, String msgType) {
MessageFactory messageFactory = messageFactories.get(beginString);
if (beginString.equals(BEGINSTRING_FIXT11)) {
// The default message factory assumes that only FIX 5.0 will be
// used with FIXT 1.1 sessions. A more flexible approach will require
// an extension to the QF JNI API. Until then, you will need a custom
// message factory if you want to use application messages prior to
// FIX 5.0 with a FIXT 1.1 session.
//
// TODO: how do we support 50/50SP1/50SP2 concurrently?
//
// If you need to determine admin message category based on a data
// dictionary, then use a custom message factory and don't use the
// static method used below.
if (!MessageUtils.isAdminMessage(msgType)) {
messageFactory = messageFactories.get(FIX50);
if (beginString.equals(BEGINSTRING_FIXT11) && !MessageUtils.isAdminMessage(msgType)) {
if (applVerID == null) {
applVerID = new ApplVerID(defaultApplVerID.getValue());
}
messageFactory = messageFactories.get(MessageUtils.toBeginString(applVerID));
}

if (messageFactory != null) {
return messageFactory.create(beginString, msgType);
return messageFactory.create(beginString, applVerID, msgType);
}

Message message = new Message();
Expand Down
14 changes: 14 additions & 0 deletions quickfixj-core/src/main/java/quickfix/MessageFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

package quickfix;

import quickfix.field.ApplVerID;

/**
* Used by a Session to create a Message.
*
Expand All @@ -35,6 +37,18 @@ public interface MessageFactory {
*/
Message create(String beginString, String msgType);

/**
* Creates a message for a specified type, FIX version, and ApplVerID.
*
* @param beginString the FIX version (for example, "FIX.4.2")
* @param applVerID the ApplVerID (for example "6" for FIX44)
* @param msgType the FIX message type (for example, "D" for an order)
* @return a message instance
*/
default Message create(String beginString, ApplVerID applVerID, String msgType) {
return create(beginString, msgType);
}

/**
* Creates a group for the specified parent message type and
* for the fields with the corresponding field ID
Expand Down
2 changes: 1 addition & 1 deletion quickfixj-core/src/main/java/quickfix/MessageUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ public static Message parse(Session session, String messageString) throws Invali
final DataDictionary applicationDataDictionary = ddProvider == null ? null : ddProvider
.getApplicationDataDictionary(applVerID);

final quickfix.Message message = messageFactory.create(beginString, msgType);
final quickfix.Message message = messageFactory.create(beginString, applVerID, msgType);
final DataDictionary payloadDictionary = MessageUtils.isAdminMessage(msgType)
? sessionDataDictionary
: applicationDataDictionary;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

import static org.junit.Assert.*;
import static quickfix.FixVersions.*;
import static quickfix.field.ApplVerID.*;

import org.junit.Test;

import quickfix.field.LinesOfText;
import quickfix.field.MsgType;
import quickfix.field.NoLinesOfText;
import quickfix.field.NoMDEntries;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import quickfix.field.*;
import quickfix.test.util.ExpectedTestFailure;

/**
Expand All @@ -17,8 +17,19 @@
* @author toli
* @version $Id$
*/
@RunWith(Parameterized.class)
public class DefaultMessageFactoryTest {
private final DefaultMessageFactory factory = new DefaultMessageFactory();
private final DefaultMessageFactory factory;
private final Class<? extends Message> fixtCreateExpectedClass;

public DefaultMessageFactoryTest(String defaultApplVerID, Class<? extends Message> fixtCreateExpectedClass) {
if (defaultApplVerID != null) {
this.factory = new DefaultMessageFactory(defaultApplVerID);
} else {
this.factory = new DefaultMessageFactory();
}
this.fixtCreateExpectedClass = fixtCreateExpectedClass;
}

@Test
public void testMessageCreate() throws Exception {
Expand All @@ -27,18 +38,28 @@ public void testMessageCreate() throws Exception {
assertMessage(quickfix.fix42.Advertisement.class, MsgType.ADVERTISEMENT, factory.create(BEGINSTRING_FIX42, MsgType.ADVERTISEMENT));
assertMessage(quickfix.fix43.Advertisement.class, MsgType.ADVERTISEMENT, factory.create(BEGINSTRING_FIX43, MsgType.ADVERTISEMENT));
assertMessage(quickfix.fix44.Advertisement.class, MsgType.ADVERTISEMENT, factory.create(BEGINSTRING_FIX44, MsgType.ADVERTISEMENT));
assertMessage(quickfix.fix50.Advertisement.class, MsgType.ADVERTISEMENT, factory.create(FIX50, MsgType.ADVERTISEMENT));
assertMessage(quickfix.fix50.Advertisement.class, MsgType.ADVERTISEMENT, factory.create(FixVersions.FIX50, MsgType.ADVERTISEMENT));
assertMessage(quickfix.fix50sp1.Advertisement.class, MsgType.ADVERTISEMENT, factory.create(FixVersions.FIX50SP1, MsgType.ADVERTISEMENT));
assertMessage(quickfix.fix50sp2.Advertisement.class, MsgType.ADVERTISEMENT, factory.create(FixVersions.FIX50SP2, MsgType.ADVERTISEMENT));
assertMessage(quickfix.Message.class, MsgType.ADVERTISEMENT, factory.create("unknown", MsgType.ADVERTISEMENT));
}

@Test
public void testFixtCreate() throws Exception {
assertMessage(quickfix.fixt11.Logon.class, MsgType.LOGON, factory.create(BEGINSTRING_FIXT11, MsgType.LOGON));
assertMessage(fixtCreateExpectedClass, MsgType.EMAIL, factory.create(BEGINSTRING_FIXT11, MsgType.EMAIL));
assertMessage(quickfix.fix40.Email.class, MsgType.EMAIL, factory.create(BEGINSTRING_FIXT11, new ApplVerID(FIX40), MsgType.EMAIL));
assertMessage(quickfix.fix41.Email.class, MsgType.EMAIL, factory.create(BEGINSTRING_FIXT11, new ApplVerID(FIX41), MsgType.EMAIL));
assertMessage(quickfix.fix42.Email.class, MsgType.EMAIL, factory.create(BEGINSTRING_FIXT11, new ApplVerID(FIX42), MsgType.EMAIL));
assertMessage(quickfix.fix43.Email.class, MsgType.EMAIL, factory.create(BEGINSTRING_FIXT11, new ApplVerID(FIX43), MsgType.EMAIL));
assertMessage(quickfix.fix44.Email.class, MsgType.EMAIL, factory.create(BEGINSTRING_FIXT11, new ApplVerID(FIX44), MsgType.EMAIL));
assertMessage(quickfix.fix50.Email.class, MsgType.EMAIL, factory.create(BEGINSTRING_FIXT11, new ApplVerID(ApplVerID.FIX50), MsgType.EMAIL));
assertMessage(quickfix.fix50sp1.Email.class, MsgType.EMAIL, factory.create(BEGINSTRING_FIXT11, new ApplVerID(ApplVerID.FIX50SP1), MsgType.EMAIL));
assertMessage(quickfix.fix50sp2.Email.class, MsgType.EMAIL, factory.create(BEGINSTRING_FIXT11, new ApplVerID(ApplVerID.FIX50SP2), MsgType.EMAIL));
}

@Test
public void testGroupCreate() throws Exception {

new ExpectedTestFailure(IllegalArgumentException.class, "unknown") {
protected void execute() throws Throwable {
factory.create("unknown", MsgType.NEWS, LinesOfText.FIELD);
Expand All @@ -50,7 +71,9 @@ protected void execute() throws Throwable {
assertEquals(quickfix.fix42.News.LinesOfText.class, factory.create(BEGINSTRING_FIX42, MsgType.NEWS, LinesOfText.FIELD).getClass());
assertEquals(quickfix.fix43.News.LinesOfText.class, factory.create(BEGINSTRING_FIX43, MsgType.NEWS, LinesOfText.FIELD).getClass());
assertEquals(quickfix.fix44.News.LinesOfText.class, factory.create(BEGINSTRING_FIX44, MsgType.NEWS, LinesOfText.FIELD).getClass());
assertEquals(quickfix.fix50.News.NoLinesOfText.class, factory.create(FIX50, MsgType.NEWS, NoLinesOfText.FIELD).getClass());
assertEquals(quickfix.fix50.News.NoLinesOfText.class, factory.create(FixVersions.FIX50, MsgType.NEWS, NoLinesOfText.FIELD).getClass());
assertEquals(quickfix.fix50sp1.News.NoLinesOfText.class, factory.create(FixVersions.FIX50SP1, MsgType.NEWS, NoLinesOfText.FIELD).getClass());
assertEquals(quickfix.fix50sp2.News.NoLinesOfText.class, factory.create(FixVersions.FIX50SP2, MsgType.NEWS, NoLinesOfText.FIELD).getClass());
assertNull("if group can't be created return null",
factory.create(BEGINSTRING_FIX40, MsgType.MARKET_DATA_SNAPSHOT_FULL_REFRESH, NoMDEntries.FIELD));
}
Expand All @@ -59,4 +82,18 @@ private static void assertMessage(Class<?> expectedMessageClass, String expected
assertEquals(expectedMessageClass, message.getClass());
assertEquals(expectedMessageType, message.getHeader().getString(MsgType.FIELD));
}

@Parameterized.Parameters(name = "defaultApplVerID = {0}")
public static Object[][] getParameters() {
return new Object[][] {
{ApplVerID.FIX40, quickfix.fix40.Email.class},
{ApplVerID.FIX41, quickfix.fix41.Email.class},
{ApplVerID.FIX42, quickfix.fix42.Email.class},
{ApplVerID.FIX43, quickfix.fix43.Email.class},
{ApplVerID.FIX44, quickfix.fix44.Email.class},
{ApplVerID.FIX50, quickfix.fix50.Email.class},
{ApplVerID.FIX50SP1, quickfix.fix50sp1.Email.class},
{ApplVerID.FIX50SP2, quickfix.fix50sp2.Email.class}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,16 @@ public void onMessage(quickfix.fix50.NewOrderSingle message, SessionID sessionID
process(message, sessionID);
}

public void onMessage(quickfix.fix50sp1.NewOrderSingle message, SessionID sessionID)
throws FieldNotFound, UnsupportedMessageType, IncorrectTagValue {
process(message, sessionID);
}

public void onMessage(quickfix.fix50sp2.NewOrderSingle message, SessionID sessionID)
throws FieldNotFound, UnsupportedMessageType, IncorrectTagValue {
process(message, sessionID);
}

public void onMessage(quickfix.fix50.SecurityDefinition message, SessionID sessionID)
throws FieldNotFound, UnsupportedMessageType, IncorrectTagValue {
try {
Expand Down