Programming Guide

Introduction

OnixS Java CME iLink 3 Binary Order Entry Handler is a Java class library that encapsulates the CME iLink 3 order entry protocol.

Main Features

  • Access to CME Market Segment Gateways (MSGW).
  • Easy-to-use API.
  • Fault Tolerance.
  • Multiple Threading Models.
  • Message Gap Detection and Retransmission Handling.
  • Flexible Logging.
  • Pre-Certified.
  • Session Scheduling (currently not supported).
  • Automated Downloading of GTC and GTD Orders.
  • Complies with CME Audit Trail Requirements.

Services

The Handler provides the following services:

  • Manages network connections.
  • Implements Session Security/Authentication.
  • Supports FIX Simple Binary Encoding (SBE), delivering faster encoding/decoding.
  • Supports FIXP Performance (FIXP) session management point-to-point session layer protocol.
  • Manages the FIXP (iLink 3) application layer (defines business-related data content).
  • Creates (outgoing) SBE messages.
  • Parses (incoming) SBE messages.
  • Persists messages and session state.
  • Recovers the iLink 3 session state after restarting.
  • Detects the telecommunication link failures and reconnects automatically.

The following image is a high-level overview of the handler architecture:

high-level-architecture

Getting Started

All handler classes are located in the biz.onixs.cme.ilink3.handler package.

The typical way of using the handler is as follows:

  1. Create the biz.onixs.cme.ilink3.handler.session.SessionSettings object and set corresponding settings.
  2. Create the biz.onixs.cme.ilink3.handler.Session object.
  3. Establish the iLink 3 session to the CME Market Segment Gateway (MSGW).
  4. Send and receive application-level SBE messages.
  5. Disconnect the session.

Check also following chapters:

  1. Error Reporting
  2. Licensing

iLink 3 Session

A iLink 3 session is defined as a bi-directional stream of ordered messages between two parties within a continuous sequence number series. It is represented by the biz.onixs.cme.ilink3.handler.Session class.

Each iLink 3 session is identified by a Universally Unique ID (UUID).

Constructing Session

To create a session the appropriate constructor must be used:

Example

29
30
31
32
33
34
35
final int MarketSegmentId = 54;
final SessionSettings settings = new SessionSettings()
        .setSession("SessionId")
        .setSecretKey("secretKey")
        .setAccessKey("accessKey")
        .setFirm("firmId");
final Session session = new Session(settings, MarketSegmentId);

Universally Unique Identifier (UUID)

Each iLink 3 session is represented with a unique 64-bit identifier (UUID). CME Group recommends using the system timestamp, which represents the number of microseconds since epoch (Jan 1, 1970) as the timestamp. Business messages received from the exchange are recoverable for the sequence number + UUID combination.

The Session generates the UUID automatically, when it is created or when biz.onixs.cme.ilink3.handler.Session.reset(boolean) method is called.

Additionally, one can pass a value manually to the biz.onixs.cme.ilink3.handler.Session.Session(long, SessionSettings, int, boolean, SessionStorageType) constructor.

More details can be found in the Universally Unique Identifier (UUID) article.

Re-Setting UUID

To reset the UUID intra-session on the same trading day, use the reset(false) method.

To reset the UUID before the first connection of the week (so-called “Weekly Reset”), use the reset(true) method.

Note: The biz.onixs.cme.ilink3.handler.Session.reset(boolean) method can be called only when the session is disconnected.

Establishing Connection

To establish an iLink 3 connection, the biz.onixs.cme.ilink3.handler.Session.logon(String, int) method must be used.

To handle network errors during the connection establishment, one needs to catch LogonException exceptions from the biz.onixs.cme.ilink3.handler.Session.logon(String, int) method and try to connect again.

To terminate the session, the biz.onixs.cme.ilink3.handler.Session.terminate() method must be used.

Reconnection Facility

When an session is in the active state and there are some network issues the handler will try to restore the connection in accordance with biz.onixs.cme.ilink3.handler.session.SessionSettings.setReconnectAttempts(int) and biz.onixs.cme.ilink3.handler.session.SessionSettings.setReconnectInterval(long) settings.

Note: Reconnection facility works for already established connections. In other cases, you need to manually handle

biz.onixs.cme.ilink3.handler.exceptions.LogonException exceptions of biz.onixs.cme.ilink3.handler.Session.logon(String, int) method and try to connect again.

Example

30
31
32
33
34
35
36
37
38
39
40
41
42
43
final int MarketSegmentId = 54;
final String Host = "Localhost";
final int Port = 64124;
final SessionSettings settings = new SessionSettings()
        .setSession("SessionId")
        .setSecretKey("secretKey")
        .setAccessKey("accessKey")
        .setFirm("firmId");
final Session session = new Session(settings, MarketSegmentId);
// Sends the Negotiation/Establishment messages and waits for the Negotiation/Establishment acknowledgments.
session.logon(Host, Port);
// Message exchanges and processing logic goes here.
// Sends the Terminate message and waits for the confirming Terminate.
session.terminate();

Exchanging Messages

Sending Messages to a Counterparty

To send a message to a counterparty, the biz.onixs.cme.ilink3.handler.Session.send(IMessage) method must be used.

The session should be fully established before sending any messages. This method is asynchronous, so the handler tries to send an SBE message in the primary application thread and only after a failure, stores it in the sending queue for the next process in the sending thread.

The Handler sets all session-level fields automatically (e.g., SeqNum, SendingTimeEpoch).

Receiving Messages from CounterParty

To receive incoming application-level messages it is necessary to implement the biz.onixs.cme.ilink3.handler.session.InboundApplicationMessageListener interface and register the handler with the biz.onixs.cme.ilink3.handler.Session.setInboundApplicationMessageListener(InboundApplicationMessageListener) method.

To receive incoming session-level messages it is necessary to implement the biz.onixs.cme.ilink3.handler.session.InboundSessionMessageListener interface and register the handler with the biz.onixs.cme.ilink3.handler.Session.setInboundSessionMessageListener(InboundSessionMessageListener) method.

Example

33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
private static class MessageProcessor implements
        InboundApplicationMessageListener,
        InboundSessionMessageListener {
  
    @Override
    public void onInboundApplicationMessage(final Object sender, final InboundApplicationMessageArgs args) {
        System.out.println("Incoming application-level message: " + args.getMsg());
    }
  
    @Override
    public void onInboundSessionMessage(final Object sender, final InboundSessionMessageArgs args) {
        System.out.println("Incoming session-level message: " + args.getMsg());
    }
}
  
public static void main(final String[] args) {
    final Handler handler = Handler.init();
    // START SNIPPET: SNIPPET
    final int MarketSegmentId = 54;
    final String Host = "Localhost";
    final int Port = 64124;
    final SessionSettings settings = new SessionSettings()
            .setSession("SessionId")
            .setSecretKey("secretKey")
            .setAccessKey("accessKey")
            .setFirm("firmId");
    final Session session = new Session(settings, MarketSegmentId);
    final MessageProcessor processor = new MessageProcessor();
    // Register message listeners
    session.setInboundApplicationMessageListener(processor);
    session.setInboundSessionMessageListener(processor);
    // Sends the Negotiation/Establishment messages and waits for the Negotiation/Establishment acknowledgments.
    session.logon(Host, Port);
    // Message exchanges and processing logic goes here.
    final IMessage message = createOrder();
    session.send(message);
    // Sends the Terminate message and waits for the confirming Terminate.
    session.terminate();

Message Sequence Numbers

Understanding Message Sequencing

Each iLink 3 Session establishes independent incoming and outgoing message sequence numbers series.

Sequence numbers are initialized at the start of an iLink 3 logical session, starting at 1 and incremented by each application-level message through the session.

A single iLink 3 logical session with the same Universally Unique Identifier (UUID) can exist across multiple sequential (not concurrent) physical connections. The client can connect and disconnect multiple times while maintaining the same iLink 3 logical session.

Once the biz.onixs.cme.ilink3.handler.Session.terminate() method is called, the physical connection is terminated, but the logical session does not end its lifetime. It is possible to continue the session later, using the biz.onixs.cme.ilink3.handler.Session.logon(String, int) method again.

fix-session-sequence-numbers

Manipulating Message Sequence Numbers

Monitoring of the sequence numbers enables parties to synchronize applications gracefully, while reconnecting during an iLink 3 session. The expected sequence number of the next incoming message can be get using the biz.onixs.cme.ilink3.handler.Session.getInSeqNum() method and set using the biz.onixs.cme.ilink3.handler.Session.setInSeqNum(long) method.

The sequence number of the next outgoing message can be get using the biz.onixs.cme.ilink3.handler.Session.getOutSeqNum() method and set using the biz.onixs.cme.ilink3.handler.Session.setOutSeqNum(long) method.

Resetting inbound and outbound message sequence numbers back to 1, for whatever reason, constitutes the beginning of a new iLink 3 session. The biz.onixs.cme.ilink3.handler.Session.reset(boolean) method must be used to back up the previous log files and reset the sequence numbers to 1. In such cases, a new UUID is generated.

Note: Both biz.onixs.cme.ilink3.handler.Session.setInSeqNum(long) and biz.onixs.cme.ilink3.handler.Session.setOutSeqNum(long) properties can be used to set expected sequence numbers manually before a FIX connection is established. However, it is NOT recommended to use these properties to reset the message sequence numbers to 1 for the session. Instead, method biz.onixs.cme.ilink3.handler.Session.reset(boolean) must be used.

SBE Message

The biz.onixs.sbe.IMessage interface represents a Simple Binary Encoding (SBE) message.

The details about SBE can be found at the iLink 3 - Simple Binary Encoding page.

Creating message

If message buffer was reused to reduce GC load, there is a need to clear it before creating a new message. The following example shows how to calculate required message size:

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public static int calculateMessageSize(final MessageSchema messageSchema, final int templateId,
                                       final Map<Integer, Integer> groupSizes) {
    int size = 0;
  
    final int framingHeaderSize = messageSchema.getFrameHeaderSize();
    final int binaryEncodingHeaderSize = messageSchema.getHeaderType().getFixedSize();
    size +=  framingHeaderSize + binaryEncodingHeaderSize;
  
    final UnifiedMetaData msgDef = messageSchema.getMessages().get(templateId);
    size += msgDef.getBlockLength();
  
    for (UnifiedMetaData groupDef : msgDef.getGroups()) {
        final int groupSize = groupSizes.getOrDefault(groupDef.getTag(), 0);
        size += groupDef.getResolvedDimensionType().getFixedSize() + groupDef.getBlockLength() * groupSize;
    }
  
    return size;
}
  
public static biz.onixs.sbe.IMessage createMessage(final MessageSchema messageSchema, final int templateId,
                                                       final Map<Integer, Integer> groupSizes,
                                                       final ByteDecoder decoder, final byte[] buffer, final int offset) throws Exception {
  
    final int size = calculateMessageSize(messageSchema, templateId, groupSizes);
    Arrays.fill(buffer, offset, offset + size, (byte) 0);
    return decoder.encodePreCreatedMessage(buffer, offset, size, templateId);
}

Working with fields

Each message or repeating group implements biz.onixs.sbe.IFieldSet interface. This interface has a couple of getXX/tryGetXX/setXX methods for primitive types, build-in composites and repeating groups. The XX suffix means type-denoting suffix that qualifies type of the data that is used by appropriate method. For instance: biz.onixs.sbe.IFieldSet.setShort(int, int), biz.onixs.sbe.IFieldSet.getLong(int), biz.onixs.sbe.IFieldSet.tryGetLong(int, AtomicLong), etc.

To access raw byte values there are biz.onixs.sbe.IFieldSet.getBytes(int), biz.onixs.sbe.IFieldSet.setBytes(int, byte:A), biz.onixs.sbe.IFieldSet.tryGetBytes(int); and biz.onixs.sbe.IFieldSet.getValuePtr(int), biz.onixs.sbe.IFieldSet.tryGetValuePtr(int, ValuePtr) methods.

IFieldSet.getXX(..) methods that return an object allocates memory and is not GC-friendly. IFieldSet.tryGetXX(..) methods accept a returning object as a parameter and do not allocate memory. Exceptions are IFieldSet.tryGetXX(..) methods which returns immutable objects like strings.

If field type can be implicitly converted to getter type, such getter will return a value or throw an exception otherwise. So biz.onixs.sbe.IFieldSet.getLong(int) can be called for fields with type byte, short, int and long; and so on.

If setter type can be implicitly converted to the field type, such setter will set a value of the field or throw an exception otherwise. So biz.onixs.sbe.IFieldSet.setBytes(int, byte:A) can be called for fields with type byte, short, int and long; and so on.

To reset values of optional fields, there is an biz.onixs.sbe.IFieldSet.reset(int) method. If the field is optional, this method will set it to the null value that is specified in the template. If the field is required, this method will set the field to the default (zero) value.

Working with repeating groups

The biz.onixs.sbe.IGroup interface represents a repeating group. Each group object implements an iterator pattern to iterate via group entries and also implements biz.onixs.sbe.IFieldSet interface to access fields of the current repeating group entry. Repeating group object can be obtained via biz.onixs.sbe.IFieldSet.getGroup(int) method.

Example of reading data from the repeating group

35
36
37
38
39
40
41
final IGroup group = message.getGroup(TAG_GROUP);
group.rewind();
while (group.next()) {
    final int value1 = group.getInt(TAG1);
    final String value2 = group.getString(TAG2);
    final ScaledDecimal value3 = group.getDecimal(TAG3);
}

Example of writing data to the repeating group.

49
50
51
52
53
54
55
56
57
58
59
60
61
62
final IGroup group = message.getGroup(TAG_GROUP);
  
group.setLength(2); // two repeating group entries
group.rewind();
  
group.next(); // first entry
group.setInt(TAG1, 10);
group.setString(TAG2, "A1");
group.setDecimal(TAG3, new ScaledDecimal(10, -1));
  
group.next(); // second entry
group.setInt(TAG1, 20);
group.setString(TAG2, "A2");
group.setDecimal(TAG3, new ScaledDecimal(20, -1));

Working with decoder

The biz.onixs.sbe.ByteDecoder class represents a SBE codec used to decode and encode SBE messages. This class is not thread safe, so each thread should use its own codec object.

ByteDecoder has methods biz.onixs.sbe.ByteDecoder.encode(byte:A, int, int, int) and biz.onixs.sbe.ByteDecoder.decode(byte:A, int, int) which creates a new message objects.

To reduce GC load there are methods biz.onixs.sbe.ByteDecoder.encodePreCreatedMessage(byte:A, int, int, int) and biz.onixs.sbe.ByteDecoder.decodePreCreatedMessage(byte:A, int, int) which initializes and returns already created message objects. If there is a need to pass message object to other thread for processing, an biz.onixs.sbe.IMessage.clone() method should be called before another call to biz.onixs.sbe.ByteDecoder.encodePreCreatedMessage(byte:A, int, int, int) or biz.onixs.sbe.ByteDecoder.decodePreCreatedMessage(byte:A, int, int).

Error Reporting

An exception is used as a fundamental error-reporting mechanism. In the event of any error the biz.onixs.cme.ilink3.handler.exceptions.HandlerException exception is thrown.

Licensing

The handler requires a valid license key file for its execution. If there is no license file available or the handler is not able to locate it, the biz.onixs.cme.ilink3.handler.exceptions.LicenseException exception will be thrown.

The pre-defined license file name is “OnixS.lic”. The license file is loaded during handler initialization as described in the Unified Resource Loading .

The default license file name can be changed using LicenseFile parameter in the Properties File or property of the Settings Class or using biz.onixs.cme.ilink3.handler.HandlerSettings.setLicenseFile(String) method.

The following table explains different options to keep the license file.

License File Location License File Name LicenseFile Property Note
application classpath OnixS.lic no change required
current directory *.lic no change required Any file with the “.lic” extension in the current directory is checked automatically.
user home directory *.lic no change required Any file with the “.lic” extension in the user home is checked automatically.
application classpath AltName.ext AltName.ext
absolute path “/foo/bar” AltName.ext /foo/bar/AltName.ext
relative path “foo/bar” AltName.ext foo/bar/AltName.ext

Configuring the Handler

There are a number of parameters (settings) which control the behavior of the handler.

To specify a configuration for the handler, one can use the biz.onixs.cme.ilink3.handler.session.SessionSettings class interface to set it programmatically.

Example

Configure the Handler by the biz.onixs.cme.ilink3.handler.session.SessionSettings class:

29
30
31
32
33
34
35
final int MarketSegmentId = 54;
final SessionSettings settings = new SessionSettings()
        .setSession("SessionId")
        .setSecretKey("secretKey")
        .setAccessKey("accessKey")
        .setFirm("firmId");
final Session session = new Session(settings, MarketSegmentId);

Unified Resource Loading

By default, any external resource is looked for at the following places and in the following order (if other is not set explicitly): 1. Classpath 1. Absolute file path 1. Current directory 1. User home directory

If it can’t be found at the 1st location then the 2nd one is checked, etc.

Logging Services

This section explains how logging services are organized in the handler. It depicts the structure of logs, as well as how logging can be customized.

Selecting Storage for Session Messages Logging

By default, the handler stores all incoming and outgoing messages, session state data, as well as the handler primary log in separate files. It uses file system directory specified by the “Log.Directory” configuration parameter.

However, it is possible to store all session-related logging data in memory. For these purposes, storageType parameter of the biz.onixs.cme.ilink3.handler.Session.Session(long, SessionSettings, boolean, SessionStorageType) constructor is available. Once biz.onixs.cme.ilink3.handler.storage.SessionStorageType.MemoryBasedStorage value will be specified as value for the storageType parameter, session-related data will be logged into memory.

Also, you can use the asynchronous File-Based Session Storage which combines good performance with the File-Based Session Storage functionality, in this case, the biz.onixs.cme.ilink3.handler.storage.SessionStorageType.AsyncFileBased value should be specified as value for the storageType parameter.

File-based Logs Structure

For each iLink3 session with file-based session storage the following files are also created:

  • SESSION_NAME.Rx.summary – contains inbound and outbound SBE messages. The sent messages could be read from this file and re-send again on counterparty request.
  • SESSION_NAME.state – contains session state data.

Where:

Restoring Session State from Log Files

When the biz.onixs.cme.ilink3.handler.Session object with a file-based session storage is created anew after an handler restart, the iLink 3 Session state (the sequence numbers of last received and sent messages, the previously sent messages, etc) is restored from these files.

To start an iLink 3 session as a new one (so-called “clean start”), it is necessary to remove the session files from the previous runs.

Connecting parties must bi-laterally agree as to when sessions are to be started/stopped, and log files are backed up based upon the individual system and time zone requirements.

When the iLink 3 session is finished, the session files are not needed anymore. They can be backed up, and later a new iLink 3 session with the new UUID will start sequence numbers from 1. The usual CME practice is to back up the log files at the end of each business week (so-called “End Of Week procedure”) to start the sequence numbers from 1 at the beginning of the next week.

Note: To reset the sequence numbers to 1 and back up, the log files biz.onixs.cme.ilink3.handler.Session.reset(boolean) method must be used. This method can be called only when the session is disconnected.

Controlling Handler Logging

By default, the handler logs all important aspects of its activity while working. The SLF4J (Simple Logging Facade for Java) is used by Handler internally. The SLF4J detects and uses the concrete logging implementation configured by the user. By default, the Logback logging implementation is recommended.

Please see Logging section for more details.

Advanced Programming

This section provides information about more advanced development techniques that are supported by Java CME iLink 3 Handler.

iLink3 Sessions

This section provides information about more advanced development techniques related to the iLink 3 session.

Understanding Session States

During the iLink 3 session lifetime, state changes occur. The biz.onixs.cme.ilink3.handler.Session class exposes the biz.onixs.cme.ilink3.handler.Session.getState() method to determine in which state it currently resides.

The biz.onixs.cme.ilink3.handler.SessionState class reflects all possible iLink 3 session states that can occur during its lifetime, as well as what a specific state means.

Tracking Session State Change

To be notified about the changes in the session state implement the biz.onixs.cme.ilink3.handler.session.StateChangeListener class and register it as event handler with the biz.onixs.cme.ilink3.handler.Session.addStateChangeListener(StateChangeListener) method.

Example

28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class SessionStateChangeListener implements StateChangeListener {
  
    public static void main(final String[] args) {
        final Handler handler = Handler.init();
        final int MarketSegmentId = 54;
        final SessionSettings settings = new SessionSettings()
                .setSession("SessionId")
                .setSecretKey("secretKey")
                .setAccessKey("accessKey")
                .setFirm("firmId");
        final Session session = new Session(settings, MarketSegmentId);
        session.addStateChangeListener(new SessionStateChangeListener());
        handler.shutdown();
    }
  
    @Override
    public void onStateChange(final Object sender, final StateChangeArgs args) {
        System.out.println("Prev session state: " + args.getPrevState());
        System.out.println("New session state: " + args.getNewState());
    }
}

Session Events and Listeners

The events and their listeners of the biz.onixs.cme.ilink3.handler.Session class are listed below:

Avoid time-consuming tasks and session management method calls (logon, terminate, reset, etc.) in the session event listener calling thread.

Example

26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
public class SessionEventListener implements StateChangeListener,
        InboundSessionMessageListener, InboundApplicationMessageListener,
        OutboundSessionMessageListener, OutboundApplicationMessageListener,
        WarningListener, ErrorListener {
  
    public static void main(final String[] args) {
        final Handler handler = Handler.init();
        final int MarketSegmentId = 54;
        final SessionSettings settings = new SessionSettings()
                .setSession("SessionId")
                .setSecretKey("secretKey")
                .setAccessKey("accessKey")
                .setFirm("firmId");
        final Session session = new Session(settings, MarketSegmentId);
        final SessionEventListener listener = new SessionEventListener();
        session.addStateChangeListener(listener);
        session.setInboundSessionMessageListener(listener);
        session.setInboundApplicationMessageListener(listener);
        session.setOutboundSessionMessageListener(listener);
        session.setOutboundApplicationMessageListener(listener);
        session.setWarningListener(listener);
        session.setErrorListener(listener);
        handler.shutdown();
    }
  
    @Override
    public void onStateChange(final Object sender, final StateChangeArgs args) {
        System.out.println("New session state: " + args.getNewState());
    }
  
    @Override
    public void onInboundApplicationMessage(final Object sender, final InboundApplicationMessageArgs args) {
        System.out.println("Incoming application-level message: " + args.getMsg());
    }
  
    @Override
    public void onInboundSessionMessage(final Object sender, final InboundSessionMessageArgs args) {
        System.out.println("Incoming session-level message: " + args.getMsg());
    }
  
    @Override
    public void onOutboundApplicationMessage(final Object sender, final OutboundApplicationMessageArgs args) {
        System.out.println("Outgoing application-level message: " + args.getMsg());
    }
  
    @Override
    public void onOutboundSessionMessage(final Object sender, final OutboundSessionMessageArgs args) {
        System.out.println("Outbound session-level message: " + args.getMsg());
    }
  
    @Override
    public void onWarning(final Object sender, final WarningArgs args) {
        System.out.println("Session warning: " + args.getReason());
    }
  
    @Override
    public void onError(final Object sender, final ErrorArgs args) {
        System.out.println("Session error: " + args.getReason());
    }
}

Resetting Message Sequence Numbers

Session class exposes biz.onixs.cme.ilink3.handler.Session.reset(boolean) method to reset both incoming and outgoing message local sequence numbers to 1, back up the previous log files and generate a new UUID.

Note: The biz.onixs.cme.ilink3.handler.Session.reset(boolean) member can be called only when the session is disconnected.

Memory Based Session Storage

Memory-based session storage is typically used to maintain a high-performance FIX session when persisting of session state and messages in the file system is not required.

To create such a session storageType parameter of session constructor should be set to biz.onixs.cme.ilink3.handler.storage.SessionStorageType.MemoryBasedStorage. Please see below:

37
38
final Session session = new Session(settings, MarketSegmentId, true,
        SessionStorageType.MemoryBasedStorage);

Async File-based Session Storage

Asynchronous File-Based Session Storage combines the good-performance with the File-Based Session Storage functionality. This storage has the same abilities as File-Based Session Storage except for the asynchronous file operations. Each this storage creates the separated thread, which will be used to perform all operations with the file system.

To create such a session storageType parameter of session constructor should be set to biz.onixs.cme.ilink3.handler.storage.SessionStorageType.AsyncFileBased. Please see below:

37
final Session session = new Session(settings, MarketSegmentId, true, SessionStorageType.AsyncFileBased);

Gateway Emulator

To simplify development and testing, the Handler’s SDK includes the Gateway Emulator. The Emulator simulates the CME Gateway. It can accept a connection, establish a FIXP session, and send/receive session-level/application-level messages.

Example

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
public class GatewayEmulator {
  
    public static void main(final String[] args) throws Throwable {
        // Initializes the Handler.
        Handler.init();
  
        // Creates a TestUtility object, which provides tools for test setup and interactions.
        final TestUtility testUtility = new TestUtility();
  
        // Creates an Acceptor object (which extends TestAcceptorTask), responsible for handling incoming connections.
        final Acceptor acceptor = new Acceptor(testUtility);
  
        // Launches the Acceptor in a separate thread, allowing it to run concurrently.
        testUtility.launchEmulatorTask(acceptor);
  
        // Creates SessionSettings.
        final SessionSettings settings = testUtility.getSessionSettings().setSession("SN").setFirm("007");
  
        // Creates a Session object using the configured settings.
        final Session initiator = new Session(settings, 1);
  
        // Establishes a connection between the 'initiator' Session and the Acceptor's emulator.
        testUtility.connectToEmulator(initiator, acceptor.getListenPort());
  
        // Create an order and sends it to gateway emulator.
        final IMessage order = testUtility.createOrder(1);
        initiator.send(order);
  
        // Initiates a termination sequence from the 'initiator' Session.
        initiator.terminate();
  
        // Shuts down the Handler, releasing resources.
        Handler.getInstance().shutdown();
  
        // Waits for the Acceptor task and other potential test tasks to complete.
        testUtility.waitTestTasks();
  
        // Asserts the results of all completed test tasks.
        testUtility.checkTestTasks();
    }
  
    private static class Acceptor extends TestAcceptorTask {
        public Acceptor(final TestUtility testUtility) {
            super(testUtility);
        }
  
        @Override
        protected void runEmulatorTestSteps() throws Throwable {
            // Creates an Emulator object configured as an acceptor.
            final Emulator emulator = testUtility.createEmulatorAcceptor();
  
            // Accepts an incoming TCP connection and prepares for message exchange.
            emulator.acceptConnection(this);
  
            // Accepts a "Negotiate" message from the initiator.
            emulator.acceptNegotiation();
  
            // Accepts an "Establish" message, finalizing the session setup.
            emulator.acceptEstablishment(1);
  
            // Receives the order.
            final IMessage order = emulator.receiveOrder(1);
  
            // Accepts a "Terminate" message, ending the session.
            emulator.acceptTerminate();
  
            // Asserts that the Emulator's connection is now closed.
            Assertions.assertTrue(emulator.isConnectionClosed());
        }
    }
}

Audit Trail Generator

The audit trail generator tool is included in the distribution package and is located in the ‘tools’ directory. This tool can be used to generate the CME audit trail files. Please see Tools :: Audit Trail Generator section for more details.

Best Practices

This section summarizes our findings and recommends best practices to tune the different layers of the handler.

Please note that the exact benefits and effects each of these configuration choices, that will be highly dependent upon the specific applications and workloads. In this way, so we strongly recommend experimenting with the different configuration options with your workload before deploying them in a production environment.

Low Latency Best Practices

Session tuning-up

Selecting right storage

Using MemoryBasedStorage instead of FileBasedStorage boosts performance since SBE messages are stored directly in memory. Alternatively, it’s possible to use custom pluggable storage (using PluggableStorage) which does nothing on SBE message-related operations as soon as no resend requests are to be supported.

It is also recommended to use AsyncFileBasedStorage which provides same functionality as FileBasedStorage with good-performance.

Manipulating Threads Affinity

By default, threads are used by a session to send and receive SBE messages; they can be executed on any of available processors/cores. Specifying CPU affinity for each session thread may give a significant performance boost. It can be configured via the biz.onixs.cme.ilink3.handler.session.SessionSettings.setReceivingThreadAffinity(int:A) and biz.onixs.cme.ilink3.handler.session.SessionSettings.setSendingThreadAffinity(int:A) methods.

Using send spinning timeout

The biz.onixs.cme.ilink3.handler.session.SessionSettings.setSendSpinningTimeout(long) method can be used to decrease the latency of the data sending.

If the value is zero (by default) and the outgoing message cannot be sent immediately it is placed to the outgoing queue.

If the value greater than zero, the biz.onixs.cme.ilink3.handler.Session.send(IMessage) method waits for the socket sending buffer availability in the spin loop mode before placing the message to the outgoing queue (to be sent later by the sending thread). The method parameter specifies the spin loop period in nanoseconds. biz.onixs.cme.ilink3.handler.session.SessionSettings.setSendSpinningTimeout(long) using makes sense when your session sends SBE messages frequently, in this case, waiting in the loop is cheaper than the thread context switch. However please note that the spin wait increases the CPU usage so the spin wait period should not be too long.

Using session warmup

The biz.onixs.cme.ilink3.handler.Session.warmUp(IMessage) method can be used to warm up the sending path.

It makes sense if your session sends SBE messages infrequently, in this case, the sending path and associated data structures will not be in a cache and this can increase the send latency. You can periodically (a recommended period is 500 usec and less) call the biz.onixs.cme.ilink3.handler.Session.warmUp(IMessage) to avoid cache misses and keep the sending path fast.

Reusing FIX message instance for incoming messages

Object creation is an expensive operation in Java, with an impact on both performance and memory consumption. The cost varies depending on the amount of initialization that needs to be performed when the object is to be created. The handler exposes an ability to reuse the incoming message by Session. We highly recommend to turn on biz.onixs.cme.ilink3.handler.session.SessionSettings.setInboundMessageReuse(boolean) to true in order to minimize the excess object creation and garbage collection overhead.

Updating Handler Configuration

Reusing SBE message instance

A common strategy is to use the same message instance for sending multiple times. As soon as a message of a certain type is constructed, it is updated with common fields for the subsequent re-use. Afterwards, before sending it to the counterparty, it is updated with mutable fields (like price or quantity) exactly for the case and sent to the counterparty.

As mutable fields are updated, each time message is about to be sent to the counterparty.

High Throughput Best Practices

This section summarizes our findings and recommends best practices to tune the different layers of the handler for high-throughput workloads. Please note that the exact benefits and effects of each of these configuration choices will be highly dependent upon the specific applications and workloads, so we strongly recommend experimenting with the different configuration options with your workload, before deploying them in a production environment.

Session Tuning-up

There are many settings in the Session class and this section covers the only subset that relates to high-throughput workloads.

Transport Layer and the TCP/IP Stack

There are many options in the protocol stack that can affect the efficiency of the data delivery. You must understand the characteristics of the version of the stacks you are running, and that they are compatible with the versions and options on the other stacks.

Enable the Nagle’s algorithm (disable TCP_NODELAY)

Nagle’s algorithm is very useful for minimizing network overhead by concatenating packets together. We strongly recommend enabling Nagle’s algorithm for high-throughput workloads. Please consider using biz.onixs.cme.ilink3.handler.session.SessionSettings.setSocketTcpNoDelay(boolean) method to set it to true.

Support

OnixS provides online support resources to customers and development partners.

Jira

Jira is an issue logging and tracking system with release version control, records of issues/fixes and enhancements, and access to useful resource and FAQ information.

Email

We recommend using Jira for issue logging/tracking. Registration requests for Jira access and technical support are also available via email at support@onixs.biz.