[QFJ-482] Provide configuration for local ip/port binding in the Initiator Created: 02/Nov/09  Updated: 15/Nov/12  Resolved: 09/Jun/11

Status: Closed
Project: QuickFIX/J
Component/s: Engine
Affects Version/s: 1.4.0
Fix Version/s: 1.5.1

Type: New Feature Priority: Default
Reporter: Morten Kristiansen Assignee: Jörg Thönnes
Resolution: Fixed Votes: 3
Labels: None

Attachments: Zip Archive mylyn-context.zip     File svndiff.log    

 Description   

We're currently working on a FIX integration towards a large European Exchange. One of the requirements from the Exchange FIX engine, was that we needed to bind our Initiator socket to specific ip adresses for the different sessions. We're currently using a locally modified QFJ (based on tag QFJ_RELEASE_1_4_0), which now supports this feature (in addition to FIX 5.0SP2 support).

We thought we should give you our modifications and hope it (in some way) will be included in a future release of QFJ. I will attach a patch (svn diff of the tag mentioned and our modifications). All diffs are for supporting Initiator local ip binding. In addition we extended a little logging in IoSessionInitiator.java (log the Exception getMessage() in event log in addition to class name; didn't really give enough info!).

NOTE: The local ip binding configuration we added should perhaps be extended to be able to support different binding for each "SocketAddress[] socketAddresses" (we only added 1 localAddress for all socketAddresses).



 Comments   
Comment by Morten Kristiansen [ 02/Nov/09 ]

I didn't see any way to attach my svn diff file, so here is its contents:

Index: core/src/main/java/quickfix/mina/initiator/AbstractSocketInitiator.java
===================================================================
— core/src/main/java/quickfix/mina/initiator/AbstractSocketInitiator.java (revision 12810)
+++ core/src/main/java/quickfix/mina/initiator/AbstractSocketInitiator.java (revision 15471)
@@ -82,6 +82,7 @@
protected void createSessionInitiators(EventHandlingStrategy eventHandlingStrategy) throws ConfigError {
try {
createSessions();
+ SessionSettings settings = getSettings();
for (Session session : getSessionMap().values())

{ SessionID sessionID = session.getSessionID(); int reconnectingInterval = getReconnectIntervalInSeconds(sessionID); @@ -91,6 +92,23 @@ throw new ConfigError("Must specify at least one socket address"); }

+ // Check if use of socket local/bind address
+ SocketAddress localAddress = null;
+ if (settings.isSetting(sessionID, Initiator.SETTING_SOCKET_LOCAL_HOST)) {
+ String host = settings.getString(sessionID, Initiator.SETTING_SOCKET_LOCAL_HOST);
+ if ("localhost".equals(host))

{ + throw new ConfigError(Initiator.SETTING_SOCKET_LOCAL_HOST + " cannot be \"localhost\"!"); + }

+ int port = 0;
+ if (settings.isSetting(sessionID, Initiator.SETTING_SOCKET_LOCAL_PORT))

{ + port = (int) settings.getLong(sessionID, Initiator.SETTING_SOCKET_LOCAL_PORT); + }

+ localAddress = ProtocolFactory.createSocketAddress(TransportType.SOCKET, host, port);
+ if (log.isInfoEnabled())

{ + log.info("Using initiator local host: " + localAddress); + }

+ }
+
NetworkingOptions networkingOptions = new NetworkingOptions(getSettings()
.getSessionProperties(sessionID));

@@ -103,7 +121,7 @@
String keyStorePassword = SSLSupport.getKeystorePasswd(getSettings(), sessionID);

IoSessionInitiator ioSessionInitiator = new IoSessionInitiator(session,

  • socketAddresses, reconnectingInterval, getScheduledExecutorService(),
    + socketAddresses, localAddress, reconnectingInterval, getScheduledExecutorService(),
    networkingOptions, eventHandlingStrategy, getIoFilterChainBuilder(),
    sslEnabled, keyStoreName, keyStorePassword);

Index: core/src/main/java/quickfix/mina/initiator/IoSessionInitiator.java
===================================================================
— core/src/main/java/quickfix/mina/initiator/IoSessionInitiator.java (revision 12810)
+++ core/src/main/java/quickfix/mina/initiator/IoSessionInitiator.java (revision 15471)
@@ -56,13 +56,13 @@
private Future<?> reconnectFuture;

public IoSessionInitiator(Session fixSession, SocketAddress[] socketAddresses,

  • long reconnectIntervalInSeconds, ScheduledExecutorService executor,
    + SocketAddress localAddress, long reconnectIntervalInSeconds, ScheduledExecutorService executor,
    NetworkingOptions networkingOptions, EventHandlingStrategy eventHandlingStrategy,
    IoFilterChainBuilder userIoFilterChainBuilder, boolean sslEnabled, String keyStoreName,
    String keyStorePassword) throws ConfigError {
    this.executor = executor;
    try { - reconnectTask = new ConnectTask(sslEnabled, socketAddresses, userIoFilterChainBuilder, + reconnectTask = new ConnectTask(sslEnabled, socketAddresses, localAddress, userIoFilterChainBuilder, fixSession, reconnectIntervalInSeconds * 1000L, networkingOptions, eventHandlingStrategy, keyStoreName, keyStorePassword); }

    catch (GeneralSecurityException e) {
    @@ -72,6 +72,7 @@

private static class ConnectTask implements Runnable {
private final SocketAddress[] socketAddresses;
+ private final SocketAddress localAddress;
private final IoConnector ioConnector;
private final Session fixSession;
private final long reconnectIntervalInMillis;
@@ -87,11 +88,12 @@
private ConnectFuture connectFuture;

public ConnectTask(boolean sslEnabled, SocketAddress[] socketAddresses,

  • IoFilterChainBuilder userIoFilterChainBuilder, Session fixSession,
    + SocketAddress localAddress, IoFilterChainBuilder userIoFilterChainBuilder, Session fixSession,
    long reconnectIntervalInMillis, NetworkingOptions networkingOptions,
    EventHandlingStrategy eventHandlingStrategy, String keyStoreName,
    String keyStorePassword) throws ConfigError, GeneralSecurityException {
    this.socketAddresses = socketAddresses;
    + this.localAddress = localAddress;
    this.fixSession = fixSession;
    this.reconnectIntervalInMillis = reconnectIntervalInMillis;
    this.keyStoreName = keyStoreName;
    @@ -136,7 +138,11 @@
    lastReconnectAttemptTime = SystemTime.currentTimeMillis();
    SocketAddress nextSocketAddress = getNextSocketAddress();
    try {
  • connectFuture = ioConnector.connect(nextSocketAddress, ioHandler);
    + if (localAddress == null) { + connectFuture = ioConnector.connect(nextSocketAddress, ioHandler); + }

    else

    { + connectFuture = ioConnector.connect(nextSocketAddress, localAddress, ioHandler); + }

    pollConnectFuture();
    } catch (Throwable e)

    { handleConnectException(e); @@ -167,7 +173,7 @@ e = e.getCause(); }

    if ((e instanceof IOException) && (e.getMessage() != null))

    { - fixSession.getLog().onEvent(e.getMessage()); + fixSession.getLog().onEvent(e.getClass().getName() + ": " + e.getMessage()); }

    else

    { String msg = "Exception during connection"; LogUtil.logThrowable(fixSession.getLog(), msg, e); Index: core/src/main/java/quickfix/Initiator.java =================================================================== --- core/src/main/java/quickfix/Initiator.java (revision 12810) +++ core/src/main/java/quickfix/Initiator.java (revision 15471) @@ -51,4 +51,20 @@ * @see quickfix.SessionFactory#SETTING_CONNECTION_TYPE */ public static final String SETTING_SOCKET_CONNECT_PORT = "SocketConnectPort"; -}

    \ No newline at end of file
    +
    + /**
    + * Initiator setting for local/bind host. Only valid when session connection
    + * type is "initiator".
    + *
    + * @see quickfix.SessionFactory#SETTING_CONNECTION_TYPE
    + */
    + public static final String SETTING_SOCKET_LOCAL_HOST = "SocketLocalHost";
    +
    + /**
    + * Initiator setting for local/bind port. Only valid when session connection
    + * type is "initiator".
    + *
    + * @see quickfix.SessionFactory#SETTING_CONNECTION_TYPE
    + */
    + public static final String SETTING_SOCKET_LOCAL_PORT = "SocketLocalPort";
    +}

Comment by Morten Kristiansen [ 02/Nov/09 ]

And of course I included the wrong diff Here's the correct one (just delete the previous):

Index: core/src/main/java/quickfix/mina/initiator/AbstractSocketInitiator.java
===================================================================
— core/src/main/java/quickfix/mina/initiator/AbstractSocketInitiator.java (revision 926)
+++ core/src/main/java/quickfix/mina/initiator/AbstractSocketInitiator.java (working copy)
@@ -82,6 +82,7 @@
protected void createSessionInitiators(EventHandlingStrategy eventHandlingStrategy) throws ConfigError {
try {
createSessions();
+ SessionSettings settings = getSettings();
for (Session session : getSessionMap().values())

{ SessionID sessionID = session.getSessionID(); int reconnectingInterval = getReconnectIntervalInSeconds(sessionID); @@ -91,6 +92,23 @@ throw new ConfigError("Must specify at least one socket address"); }

+ // Check if use of socket local/bind address
+ SocketAddress localAddress = null;
+ if (settings.isSetting(sessionID, Initiator.SETTING_SOCKET_LOCAL_HOST)) {
+ String host = settings.getString(sessionID, Initiator.SETTING_SOCKET_LOCAL_HOST);
+ if ("localhost".equals(host))

{ + throw new ConfigError(Initiator.SETTING_SOCKET_LOCAL_HOST + " cannot be \"localhost\"!"); + }

+ int port = 0;
+ if (settings.isSetting(sessionID, Initiator.SETTING_SOCKET_LOCAL_PORT))

{ + port = (int) settings.getLong(sessionID, Initiator.SETTING_SOCKET_LOCAL_PORT); + }

+ localAddress = ProtocolFactory.createSocketAddress(TransportType.SOCKET, host, port);
+ if (log.isInfoEnabled())

{ + log.info("Using initiator local host: " + localAddress); + }

+ }
+
NetworkingOptions networkingOptions = new NetworkingOptions(getSettings()
.getSessionProperties(sessionID));

@@ -103,7 +121,7 @@
String keyStorePassword = SSLSupport.getKeystorePasswd(getSettings(), sessionID);

IoSessionInitiator ioSessionInitiator = new IoSessionInitiator(session,

  • socketAddresses, reconnectingInterval, getScheduledExecutorService(),
    + socketAddresses, localAddress, reconnectingInterval, getScheduledExecutorService(),
    networkingOptions, eventHandlingStrategy, getIoFilterChainBuilder(),
    sslEnabled, keyStoreName, keyStorePassword);

Index: core/src/main/java/quickfix/mina/initiator/IoSessionInitiator.java
===================================================================
— core/src/main/java/quickfix/mina/initiator/IoSessionInitiator.java (revision 926)
+++ core/src/main/java/quickfix/mina/initiator/IoSessionInitiator.java (working copy)
@@ -56,13 +56,13 @@
private Future<?> reconnectFuture;

public IoSessionInitiator(Session fixSession, SocketAddress[] socketAddresses,

  • long reconnectIntervalInSeconds, ScheduledExecutorService executor,
    + SocketAddress localAddress, long reconnectIntervalInSeconds, ScheduledExecutorService executor,
    NetworkingOptions networkingOptions, EventHandlingStrategy eventHandlingStrategy,
    IoFilterChainBuilder userIoFilterChainBuilder, boolean sslEnabled, String keyStoreName,
    String keyStorePassword) throws ConfigError {
    this.executor = executor;
    try { - reconnectTask = new ConnectTask(sslEnabled, socketAddresses, userIoFilterChainBuilder, + reconnectTask = new ConnectTask(sslEnabled, socketAddresses, localAddress, userIoFilterChainBuilder, fixSession, reconnectIntervalInSeconds * 1000L, networkingOptions, eventHandlingStrategy, keyStoreName, keyStorePassword); }

    catch (GeneralSecurityException e) {
    @@ -72,6 +72,7 @@

private static class ConnectTask implements Runnable {
private final SocketAddress[] socketAddresses;
+ private final SocketAddress localAddress;
private final IoConnector ioConnector;
private final Session fixSession;
private final long reconnectIntervalInMillis;
@@ -87,11 +88,12 @@
private ConnectFuture connectFuture;

public ConnectTask(boolean sslEnabled, SocketAddress[] socketAddresses,

  • IoFilterChainBuilder userIoFilterChainBuilder, Session fixSession,
    + SocketAddress localAddress, IoFilterChainBuilder userIoFilterChainBuilder, Session fixSession,
    long reconnectIntervalInMillis, NetworkingOptions networkingOptions,
    EventHandlingStrategy eventHandlingStrategy, String keyStoreName,
    String keyStorePassword) throws ConfigError, GeneralSecurityException {
    this.socketAddresses = socketAddresses;
    + this.localAddress = localAddress;
    this.fixSession = fixSession;
    this.reconnectIntervalInMillis = reconnectIntervalInMillis;
    this.keyStoreName = keyStoreName;
    @@ -136,7 +138,11 @@
    lastReconnectAttemptTime = SystemTime.currentTimeMillis();
    SocketAddress nextSocketAddress = getNextSocketAddress();
    try {
  • connectFuture = ioConnector.connect(nextSocketAddress, ioHandler);
    + if (localAddress == null) { + connectFuture = ioConnector.connect(nextSocketAddress, ioHandler); + }

    else

    { + connectFuture = ioConnector.connect(nextSocketAddress, localAddress, ioHandler); + }

    pollConnectFuture();
    } catch (Throwable e)

    { handleConnectException(e); @@ -167,7 +173,7 @@ e = e.getCause(); }

    if ((e instanceof IOException) && (e.getMessage() != null))

    { - fixSession.getLog().onEvent(e.getMessage()); + fixSession.getLog().onEvent(e.getClass().getName() + ": " + e.getMessage()); }

    else

    { String msg = "Exception during connection"; LogUtil.logThrowable(fixSession.getLog(), msg, e); Index: core/src/main/java/quickfix/Initiator.java =================================================================== --- core/src/main/java/quickfix/Initiator.java (revision 926) +++ core/src/main/java/quickfix/Initiator.java (working copy) @@ -51,4 +51,20 @@ * @see quickfix.SessionFactory#SETTING_CONNECTION_TYPE */ public static final String SETTING_SOCKET_CONNECT_PORT = "SocketConnectPort"; -}

    \ No newline at end of file
    +
    + /**
    + * Initiator setting for local/bind host. Only valid when session connection
    + * type is "initiator".
    + *
    + * @see quickfix.SessionFactory#SETTING_CONNECTION_TYPE
    + */
    + public static final String SETTING_SOCKET_LOCAL_HOST = "SocketLocalHost";
    +
    + /**
    + * Initiator setting for local/bind port. Only valid when session connection
    + * type is "initiator".
    + *
    + * @see quickfix.SessionFactory#SETTING_CONNECTION_TYPE
    + */
    + public static final String SETTING_SOCKET_LOCAL_PORT = "SocketLocalPort";
    +}

Comment by Morten Kristiansen [ 16/Jan/10 ]

SVN diff attached

Comment by Jörg Thönnes [ 19/Jan/11 ]

This is an important feature to connect to systems protected by a firewall.

Therefore, I would like to see this for the next version, ie 1.5.1.

Comment by Eric Deshayes [ 28/Apr/11 ]

committed in the integration branch rev #1023

Comment by Jörg Thönnes [ 09/Jun/11 ]

Re-opening because the new configuration options SocketLocalHost and SocketLocalPort are not documented.

Comment by Jörg Thönnes [ 09/Jun/11 ]

Resolving as fixed since it is documented now.

Close as soon the release is complete.

Generated at Sat May 18 20:22:56 UTC 2024 using JIRA 7.5.2#75007-sha1:9f5725bb824792b3230a5d8716f0c13e296a3cae.