Hello there!
I'll take you up on the offer to keep you updated on my findings. I've been experimenting a bit more and feel I have a few points for you to consider including in camel-fix.
Two bugs (I think):
1. As stated earlier, I'm pretty sure that you have mistakenly switched the code in CamelApplication.toApp() with the CamelApplication.fromApp(). fromApp() is the one that is called when the application needs to process an incoming message. I've moved the call to "endpoint.onMessage(message, sessionID);" and put it in fromApp(). This works fine.
2. The initiator is never started in FixClientEndpoint.login(). This means that the FIX client is never started. E g writing code like the following doesn't work in a unit test:
from("direct:start").to("fix:initiator.cfg");
By changing FixClientEndpoint.login() to the following, the above will work:
protected void login(SessionSettings settings, Application application, MessageStoreFactory storeFactory, LogFactory logFactory) throws Exception {
initiator = new SocketInitiator(application, storeFactory, settings, logFactory, getMessageFactory());
initiator.start();
}
I also have 4 cases where I need to configure QuickFix/J differently than what is being done in camel-fix:
1. Support for user/password.
Normally you have to add user/password in the callback CamelApplication.fromAdmin(). My version looks like this:
public void fromAdmin(Message message, SessionID sessionID)
throws FieldNotFound, IncorrectDataFormat, IncorrectTagValue,
RejectLogon {
if (LOG.isDebugEnabled()) {
+LOG.debug("fromAdmin() session: " + sessionID + " " + message);+
}
final Message.Header header = message.getHeader();
if (header.getField(new MsgType()).valueEquals(MsgType.LOGON)) {
if (endpoint.getUser() != null) {
if (LOG.isDebugEnabled()) {
+LOG.debug("fromAdmin(), logging on " + endpoint.getUser());+
}
+LOG.info("fromAdmin(), logging on " + endpoint.getUser());+
message.setField(new Username(endpoint.getUser()));
if (endpoint.getPassword() != null) {
message.setField(new Password(endpoint.getPassword()));
}
}
}
}
As you can see I assume that the user and password is accessible throught the endpoint. To get this working I've changed the FixEndpoint class as follows:
...
private String user;
private String password;
public String getUser() {
return user;
}
public String getPassword() {
return password;
}
...
public void setSessionID(SessionID sessionID) {
final String USER_KEY = "camel.user";
final String PASSWORD_KEY = "camel.password";
this.sessionID = sessionID;
if (settings.isSetting(sessionID, USER_KEY)) {
try {
user = settings.getString(sessionID, USER_KEY);
} catch (ConfigError e) {
+LOG.fatal("Could not retrieve the property " + USER_KEY, e);+
} catch (FieldConvertError e) {
+LOG.fatal("Could not retrieve the property " + USER_KEY, e);+
}
if (settings.isSetting(sessionID, PASSWORD_KEY)) {
try {
password = settings.getString(sessionID, PASSWORD_KEY);
} catch (ConfigError e) {
LOG.fatal(
+"Could not retrieve the property " + PASSWORD_KEY, e);+
} catch (FieldConvertError e) {
+LOG.fatal("Could not retrieve the property " + PASSWORD_KEY, e);+
}
}
}
}
I assume the properties "camel.user" and "camel.password" in QuickFix/J's configuration file. I think you could use this technique (with some suitable namespace) to enable Camel specific settings that make your classes more general.
2. Support for specifying what MessageStoreFactory to use.
I currently just patched FixEndpoint.createMessageStoreFactory() as follows:
protected MessageStoreFactory createMessageStoreFactory(
SessionSettings settings) {
+LOG.debug("createMessageStoreFactory: " + settings);+
return new FileStoreFactory(settings);
// return new JdbcStoreFactory(settings);
}
Ideally it should be possible to configure what MessageStoreFactory to use via a Camel specific property in QuickFix/J's configuration file. Similar to the user/password approach.
3. Support for specifying what LogFactory to use.
I currently just patched FixEndpoint.createMessageStoreFactory() as follows:
protected LogFactory createLogFactory(SessionSettings settings) {
// I use this for testing
return new CompositeLogFactory(new LogFactory[]{new ScreenLogFactory(settings), new SLF4JLogFactory(settings)} );
// This is what is being done today in camel-fix
// return new ScreenLogFactory(settings);
// This is what I will use in production
return new SLF4JLogFactory(settings);
}
Currently the ScreenLogFactory is hard coded in camel-fix. This is inadequate for production use. I think this must also be configurable via a Camel specific property in the QuickFix/J settings file.
4. - JMX support.
In production I think this is a must. In order to enable this in QuickFix/J I changed the login() method in FixClientEndpoint and FixServerEndpoint as follows:
FixClientEndpoint:
protected void login(SessionSettings settings, Application application, MessageStoreFactory storeFactory, LogFactory logFactory) throws Exception {
initiator = new SocketInitiator(application, storeFactory, settings, logFactory, getMessageFactory());
initiator.start(); // I added this line as well
JmxExporter jmxExporter = new JmxExporter();
jmxExporter.export(initiator);
}
FixServerEndpoint:
protected void login(SessionSettings settings, Application application, MessageStoreFactory storeFactory, LogFactory logFactory) throws ConfigError, JMException {
acceptor = new SocketAcceptor(application, storeFactory, settings,
logFactory, getMessageFactory());
JmxExporter jmxExporter = new JmxExporter();
jmxExporter.export(acceptor);
acceptor.start();
}
Originally I also had my own FixConverter class since the one provided with camel-fix required classes from Artix Data Services that were not open sourced (or at least not accessible). Since the release of FUSE Mediation Router 1.5.4.0 the classes seem to be included. However, the source code is not available for those classes which means that I cannot build camel-fix by myself. This doesn't seem right for an open source product.
I realize that the 4 feature requests above are needed because QuickFix/J doesn't enable everything to be configured in the settings file but requires a lot of configuration to be done in code. E g I cannot understand why QuickFix/J doesn't support specifying user/password in the initiator's configuration file. But things are as they are which means that to make camel-fix usable you have to add ways to make these things configurable without requiring it to be coded. I think you probably have to make it possible for users (like me) to be able to subclass necessary classes in camel-fix in case they are not general enough. As you can see above, I've found a number of things that I must be able to configure but I'm sure I will find more when I start using camel-fix more. I will then need a better fallback then my current which is to put my own versions of your classes first in the classpath.
At the risk of being even more troublesome for you, I have two more requests...
a) QuickFix/J 1.4 was just released with lots of bug fixes and support for FIX 5.0. It would be great if camel-fix supported (was compiled aginst) that version of QuickFix.
b) I Need to use FUSE Mediation Router with Websphere MQ and have noticed that fixes concerning this has been added to the version 2.0 branch. When is this due for release? I noticed that Apache Camel 1.6 was recently released which means that FUSE is now lagging compared to the Apache project.
My dream scenario is to have:
- The two bugs fixed
- More possibilities of configuring camel-fix
- An easy way to subclass your classes when configuration is not enough
- Support for QuickFix/J 1.4
- A quick release of FUSE Mediation Router 2.0 (or other version that includes all above camel-fix changes as well as the MQ improvements I've seen)
I'll be gone skiing for a week (without a computer) so I won't be able to answer any replies until after that.
Hope you didn't choke on this post - remember that I really like your product and it really gives value for me to be able to use it in conjunction with QuickFix/J.
Best regards,
/Bengt