|
Key
This line was removed.
This word was removed. This word was added.
This line was added.
|
Changes (4)
View page history... |
public void buildFilterChain(IoFilterChain chain) { |
| chain.addBefore(AbstractIoHandler.FIX_CODEC_FILTER_NAME, chain.addBefore(FIXProtocolCodecFactory.FILTER_NAME, "BlacklistFilter", blacklistFilter); |
| }}); acceptor.start(); |
... |
| The code changes required to use it are similar to the Blacklist filter, except that: * The filter is configured from SessionSettings. |
| * You have to add the filter after AbstractIoHandler.FIX_CODEC_FILTER_NAME FIXProtocolCodecFactory.FILTER_NAME as it relies on this to parse received messages. |
{code} |
... |
| ((SessionConnector) acceptor).setIoFilterChainBuilder(new IoFilterChainBuilder() { public void buildFilterChain(IoFilterChain chain) { |
| chain.addAfter(AbstractIoHandler.FIX_CODEC_FILTER_NAME, chain.addAfter(FIXProtocolCodecFactory.FILTER_NAME, "WhitelistFilter", whitelistFilter); |
| }}); acceptor.start(); |
... |
| WARNING: [/127.0.0.1:2633] Closing FIX.4.2:EXEC->BANZAI connection from unauthorised address. {noformat} |
{warning}Information below this point does not reflect actual implementation. The SSL Info may still be useful though.{warning} h1. Inserting MINA IoFilters It would be nice if quickfixj allowed custom IoFilters. Why? From MINA's javadoc for [IoFilter|http://directory.apache.org/subprojects/mina/apidocs/org/apache/mina/common/IoFilter.html]: {quote} A filter which intercepts IoHandler events like Servlet filters. Filters can be used for these purposes: * Event logging, * Performance measurement, * Authorization, * Overload control, * Message transformation (e.g. encryption and decryption, ...), * and many more. {quote} This page outlines one method of allowing custom filters to be specified. Note that since quickfixj uses a filter to convert the raw IO into strings it is possible to completely break quickfixj by removing this from the filter chain or otherwise interfering with its operation. h2. Overview and Intent The basic idea is to allow the user to supply simple filters without modifying existing quickfix bootstrap or quickfix.Application code. Three things are required to add custom filters: # The custom filters themselves. MINA supply some useful implementations such as LoggingFilter and SSLFilter, or you can supply your own. # A quickfix.mina.IoFilterChainBuilderFactory implementation. This is a new interface which basically has a create method to return an IoFilterChainBuilder. # A line in the config file to tell quickfix to use the custom IoFilterChainBuilder instead of the default one. h2. Changes Required See attached [^filters.zip]. Warning: this hasn't really received any testing. Here's a rundown of the changes required. h3. Create a new interface, quickfix.mina.IoFilterChainBuilderFactory. I've kept it fairly simple. Inititially I was wanting to pass the Session on the create method, but acceptors with multiple sessions complicates things a little. Something to think about. I was also gravitating between creating a new filter chain builder each time an IoSession is created and once on startup. {code:java} package quickfix.mina; import org.apache.mina.common.IoFilterChainBuilder; import org.apache.mina.filter.codec.ProtocolCodecFilter; import quickfix.ConfigError; import quickfix.SessionSettings; import quickfix.mina.message.FIXProtocolCodecFactory; /** * Allows a user to add MINA filters to quickfix sessions. * * Specify the name of a class implementing this interface as a default session property * "IoFilterChainBuilderFactory". * * Implementations of this class must have a no-arg constructor. */ public interface IoFilterChainBuilderFactory { /** * The name of the default session property to use to specify an instance of this class. */ public static final String PROPERTY_NAME = "IoFilterChainBuilderFactory"; /** * This filter must be included in the FilterChain for quickfix to work. * Add IO (ByteBuffer) type filters such as SSL filters before it. * Add protocol (String) filters after it. */ public static final ProtocolCodecFilter QUICKFIX_PROTOCOL_FILTER = new ProtocolCodecFilter(new FIXProtocolCodecFactory()); /** * Gives the factory access to the session settings. * * This method will be called before createIoFilterChainBuilder. * * @param sessionSettings quickfix Session Settings * @throws ConfigError if the supplied sessionSettings don't supply settings required for the IoFilterChainBuilderFactory. */ public void setSessionSettings(SessionSettings sessionSettings) throws ConfigError; /** * Create an IoFilterChainBuilder. * * This will be when an initiator or acceptor session is first created. * * It might be useful to have a Session parameter here to enable session specific filters, but * a single acceptor session can service multiple sessions so more thought needs to be put into it. * * @return a new IoFilterChainBuilder */ public IoFilterChainBuilder createIoFilterChainBuilder(); } {code} h3. Create a default implementation of IoFilterChainBuilderFactory Quickfix needs a default implementation to use when none is specified. This simply creates a filter chain builder with a single filter of IoFilterChainBuilderFactory.QUICKFIX_PROTOCOL_FILTER. {code:java} package quickfix.mina; import org.apache.mina.common.DefaultIoFilterChainBuilder; import org.apache.mina.common.IoFilterChainBuilder; import quickfix.SessionSettings; /** * Default implementation of IoFilterChainBuilderFactory */ public class DefaultIoFilterChainBuilderFactory implements IoFilterChainBuilderFactory { public IoFilterChainBuilder createIoFilterChainBuilder() { return createDefaultIoFilterChainBuilder(); } /** * @return a DefaultIoFilterChainBuilder which includes the fix protocol codec filter required by quickfix. * @see IoFilterChainBuilderFactory#QUICKFIX_PROTOCOL_FILTER */ protected DefaultIoFilterChainBuilder createDefaultIoFilterChainBuilder() { DefaultIoFilterChainBuilder defaultIoFilterChainBuilder = new DefaultIoFilterChainBuilder(); // may be useful to make this filter name "public" for addBefore and addAfter methods on the DefaultIoFilterChainBuilder. defaultIoFilterChainBuilder.addFirst("protocolCodecFilter", IoFilterChainBuilderFactory.QUICKFIX_PROTOCOL_FILTER); return defaultIoFilterChainBuilder; } public void setSessionSettings(SessionSettings sessionSettings) { // not needed } } {code} h3. Modify Existing Quickfix Code to Use the IoFilterChainBuilderFactory There are a few changes to make quickfix tell MINA to use the filter chain builder. Modify AbstractIOHandler - don't add filter in session created any more. Doing it here makes it impossible to add other filters before it (which we will need to do for an SSL filter) and would also add it before any MINA threadpool filters if they ever get used. Modify SessionConnector Add field IoFilterChainBuilderFactory ioFilterChainBuilderFactory. This field defaults to quickfix.mina.DefaultIoFilterChainBuilderFactory, but can be overriden via a property in the settings file. May also like to add a setter to allow filter chain builder to be set programmatically rather than via config. Add getIoFilterChainBuilder method which calls ioFilterChainBuilderFactory.createIoFilterChainBuilder(). This will be called by Acceptor and Initiator implementations. Modify ProtocolFactory Add ProtocolFactory.createConfig(IoService, IoFilterChainBuilder) which will create an IoServiceConfig with appropriate thread pool and io filter options. Modify AbstractSocketAcceptor In startAcceptingConnections add extra parameter to bind method call - ProtocolFactory.createConfig(ioAcceptor, config). Modify IoSessionInitiator Add IoFilterChainBuilder field & constructor param for it Modify connect() to get connectorConfig from ProtocolFactory.createConfig. Modify AbstractSocketInitiator Modify to pass extra constructor arg to IoSessionInitator, obtained from SessionConnector.getIoFilterChainBuilder h2. Example - More logging Ok, so we don't really need more logging, but it is a simple example that illustrates the point. The custom filter is an org.apache.mina.filter.LoggingFilter. The IoFilterChainBuilderFactory implementation looks like this: {code} package quickfix.examples.filter; import org.apache.mina.common.DefaultIoFilterChainBuilder; import org.apache.mina.common.IoFilterChainBuilder; import org.apache.mina.filter.LoggingFilter; import quickfix.mina.DefaultIoFilterChainBuilderFactory; public class LogIoFilterChainBuilderFactory extends DefaultIoFilterChainBuilderFactory { public IoFilterChainBuilder createIoFilterChainBuilder() { DefaultIoFilterChainBuilder defaultIoFilterChainBuilder = createDefaultIoFilterChainBuilder(); // add before the protocol filter to log the raw IO defaultIoFilterChainBuilder.addFirst("log raw", new LoggingFilter()); // add after the protocol filter to log the messages as strings defaultIoFilterChainBuilder.addLast("log protocol", new LoggingFilter()); return defaultIoFilterChainBuilder; } } {code} And the line in the config file like this: {noformat} IoFilterChainBuilderFactory=quickfix.examples.filter.LogIoFilterChainBuilderFactory {noformat} In this case, quickfix.examples.filter.LogIoFilterChainBuilderFactory is an IoFilterChainBuilderFactory implementation which uses the MINA logging filter to log protocol and raw IO (note lines from org.apache.mina.util.SessionLog): {noformat} <20060726-11:36:23, FIX.4.2:BANZAI->EXEC, outgoing> (8=FIX.4.29=6635=A34=1649=BANZAI52=20060726-11:36:23.95356=EXEC98=0108=3010=024) <20060726-11:36:24, FIX.4.2:BANZAI->EXEC, event> (Initiated logon request) 26/07/2006 21:36:23 quickfix.mina.initiator.InitiatorIoHandler sessionCreated INFO: MINA session created: /127.0.0.1:2191 26/07/2006 21:36:23 org.apache.mina.util.SessionLog info INFO: [localhost/127.0.0.1:9876] OPENED 26/07/2006 21:36:23 org.apache.mina.util.SessionLog info INFO: [localhost/127.0.0.1:9876] OPENED 26/07/2006 21:36:23 org.apache.mina.util.SessionLog info INFO: [localhost/127.0.0.1:9876] WRITE: 8=FIX.4.29=6635=A34=1649=BANZAI52=20060726-11:36:23.95356=EXEC98=0108=3010=024 26/07/2006 21:36:23 org.apache.mina.util.SessionLog info INFO: [localhost/127.0.0.1:9876] WRITE: DirectBuffer[pos=0 lim=88 cap=128: 38 3D 46 49 58 2E 34 2E 32 01 39 3D 36 36 01 33 35 3D 41 01<snip to prevent long line..> 26/07/2006 21:36:23 org.apache.mina.util.SessionLog info {noformat} This isn't particularly useful in most situations (though a nice formatted hexdump log would make it slightly nicer), but hopefully you get the idea. h2. Example - IoFilterChainBuilderFactory via Spring Writing a new IoFilterChainBuilderFactory implementation everytime you want a different set of filters is a bit of a drag. Lets use the declarative bean factory goodness of spring instead! {info}This example tested with Spring 1.2.7, MINA Core 0.9.5-SNAPSHOT and MINA Spring Integration 0.9.5-SNAPSHOT{info} First, we need an IoFilterChainBuilderFactory to delegate to the spring context. It reads two settings from the default session settings section - one for the name of the application context xml to load, and one for the name of the bean to use. {code:java|title=SpringIoFilterChainBuilderFactory} package quickfix.examples.filter; import org.apache.mina.common.IoFilterChainBuilder; import org.springframework.beans.BeansException; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.util.StringUtils; import quickfix.ConfigError; import quickfix.FieldConvertError; import quickfix.SessionSettings; import quickfix.mina.IoFilterChainBuilderFactory; /** * Retrieves an IoFilterChainBuilder from a Spring Application Context. */ public class SpringIoFilterChainBuilderFactory implements IoFilterChainBuilderFactory { /** * Name of config property used to specify spring context.xml. Comma seperate for multiple * files. */ public static final String APPLICATION_CONTEXT_KEY = "SpringFilterApplicationContext"; /** * Name of the bean in the spring context which is the ioFilterChainBuilder. */ public static final String BEAN_NAME_KEY = "SpringFilterBeanName"; private IoFilterChainBuilder ioFilterChainBuilder; public IoFilterChainBuilder createIoFilterChainBuilder() { return ioFilterChainBuilder; } public void setSessionSettings(SessionSettings sessionSettings) throws ConfigError { String appContext; try { appContext = sessionSettings.getString(APPLICATION_CONTEXT_KEY); } catch (FieldConvertError e) { throw new ConfigError("Unable to read setting " + APPLICATION_CONTEXT_KEY, e); } String beanName; try { beanName = sessionSettings.getString(BEAN_NAME_KEY); } catch (FieldConvertError e) { throw new ConfigError("Unable to read setting " + BEAN_NAME_KEY, e); } /* * Retrieving the application context and bean here rather than in createIoFilterChainBuilder() so * any errors are reported as soon as possible. */ try { ConfigurableApplicationContext applicationContext = new ClassPathXmlApplicationContext(StringUtils.tokenizeToStringArray(appContext, ",")); this.ioFilterChainBuilder = (IoFilterChainBuilder) applicationContext.getBean(beanName, IoFilterChainBuilder.class); } catch (BeansException e) { throw new ConfigError("Error creating spring context: " + e.getMessage(), e); } } } {code} Now we need an application context. This simple one will do the same thing as the logging example above. The org.apache.mina.integration.spring.DefaultIoFilterChainBuilderFactoryBean allows us to easily specify our filters in a list. {code:xml|title=LogFilterContext.xml} <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="ioFilterChainBuilder" class="org.apache.mina.integration.spring.DefaultIoFilterChainBuilderFactoryBean"> <property name='filters'> <list> <!-- log raw IO --> <bean class='org.apache.mina.filter.LoggingFilter' /> <!-- Must always include the quickfix protocol filter --> <bean id="quickfix.mina.IoFilterChainBuilderFactory.QUICKFIX_PROTOCOL_FILTER" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean" /> <!-- log protocol IO --> <bean class='org.apache.mina.filter.LoggingFilter' /> </list> </property> </bean> </beans> {code} Finally, add the necessary properties to your config file: {noformat} IoFilterChainBuilderFactory=quickfix.examples.filter.SpringIoFilterChainBuilderFactory SpringFilterApplicationContext=quickfix/examples/filter/LogFilterContext.xml SpringFilterBeanName=ioFilterChainBuilder {noformat} h2. Example - SSL Finally a useful example :) This example builds on the the SpringIoFilterBuilderFactory example above but uses an org.apache.mina.SSLFilter to add SSL to quickfix. The org.apache.mina.integration.spring.ssl.SSLContextFactoryBean makes it very easy to configure the SSLContext, which is the primary reason for using Spring in the example. h3. Prerequisites * Classpath Changes ** Spring - I used 1.2.7 ** MINA Core, MINA Spring Integration, MINA Filter SSL - I used 0.9.5-SNAPSHOT. * For acceptors, create a keystore - follow step #1 at http://java.sun.com/j2se/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#CreateKeystore * A truststore should be used with initiators, though this example doesn't use one. * Ideally get the SpringIoFilterBuilderFactory example above working first - then any problems should hopefully be SSL related. h3. Initiator For an initiator we need to: * Make sure we're using client mode of the SSL Engine * Supply a trust manager. In this case we're using the supplied BogusTrustManagerFactory which trusts all server certificates. {code:xml|title=initiatorSslFilterContext.xml} <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="ioFilterChainBuilder" class="org.apache.mina.integration.spring.DefaultIoFilterChainBuilderFactoryBean"> <property name='filters'> <list> <!-- SSL Filter must be first in the chain. --> <ref bean="sslFilter" /> <!-- Must always include the quickfix protocol filter --> <bean id="quickfix.mina.IoFilterChainBuilderFactory.QUICKFIX_PROTOCOL_FILTER" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean" /> </list> </property> </bean> <bean id="sslFilter" class="org.apache.mina.filter.SSLFilter"> <constructor-arg ref="sslContext" /> <property name='useClientMode' value='true' /> </bean> <bean id='sslContext' class='org.apache.mina.integration.spring.ssl.SSLContextFactoryBean'> <property name='trustManagerFactory'> <!-- !! Trust ANY server certificate !! --> <bean class="org.apache.mina.integration.spring.ssl.BogusTrustManagerFactory" /> </property> </bean> </beans> {code} Now we just need some properties in the config file: {noformat} IoFilterChainBuilderFactory=quickfix.examples.filter.SpringIoFilterChainBuilderFactory SpringFilterApplicationContext=quickfix/examples/filter/initiatorSslFilterContext.xml SpringFilterBeanName=ioFilterChainBuilder {noformat} h3. Acceptor For the acceptor we specify a key store and don't use client mode. {code:xml|title=acceptorSslFilterContext.xml} <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="placeholderConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <!-- location of keystore - will be picked up from classpath --> <property name="properties"> <props> <prop key="keystore.resource">quickfix/examples/filter/keystore</prop> <prop key="keystore.password">password</prop> </props> </property> </bean> <bean id="ioFilterChainBuilder" class="org.apache.mina.integration.spring.DefaultIoFilterChainBuilderFactoryBean"> <property name='filters'> <list> <ref bean="sslFilter" /> <!-- Must always include the quickfix protocol filter --> <bean id="quickfix.mina.IoFilterChainBuilderFactory.QUICKFIX_PROTOCOL_FILTER" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean" /> </list> </property> </bean> <bean id="sslFilter" class="org.apache.mina.filter.SSLFilter"> <constructor-arg ref="sslContext" /> <property name='useClientMode' value='false' /> </bean> <bean id='sslContext' class='org.apache.mina.integration.spring.ssl.SSLContextFactoryBean'> <property name='keyManagerFactoryKeyStore'> <bean class='org.apache.mina.integration.spring.ssl.KeyStoreFactoryBean'> <property name='resource' value='${keystore.resource}' /> <property name='password' value='${keystore.password}' /> </bean> </property> <property name='keyManagerFactoryKeyStorePassword' value='${keystore.password}' /> <!-- These won't be suitable if you're not using Sun's JVM --> <property name='keyManagerFactoryProvider' value='SunJSSE' /> <property name='keyManagerFactoryAlgorithm' value='SunX509' /> </bean> </beans> {code} Again, we need to specify the filter details in the properties file. {noformat} IoFilterChainBuilderFactory=quickfix.examples.filter.SpringIoFilterChainBuilderFactory SpringFilterApplicationContext=quickfix/examples/filter/acceptorSslFilterContext.xml SpringFilterBeanName=ioFilterChainBuilder {noformat} h3. Troubleshooting * Try adding -Djavax.net.debug=all to your vm arguments to output verbose SSL debugging information. h2. Comments & Suggestions Thanks Brad\! There is also some limited information on MINA SSL filters at [SSL Support]. [~admin] |
