Skip to content

fix8mt/ufeed_bindings_java

Repository files navigation

UFEed Java Bindings


Introduction

The UFEed Java Adapter (UFEed_Java) provides a low level java interface to the UFEGW. Interactions with the UFEGW are based around a UFEedClient object from com.fix8mt.ufe.ufeedclient namespace which can be used to send and receive Messages to and from the UFEGW.

Use the following Universal FIX engine documentaion for a reference.

Features of UFEedClient:

  • System API support (see 4. Implementation Guide - Section 1.3)

  • Business API support (eg. NewOrderSingle and standard FIX messages)

  • Provides a 4-way communications API in order to make requests, publish messages, receive responses and subscribe to broadcast messages

  • User defined implementation of callback interface to handle these PUB, SUB, REQ and REP message events

  • Dynamic configuration of PUB, SUB, REQ, REP addressing and topics

  • Internal session management

Features of a UFEMessage/UFEMessage.Builder:

  • A generic container of fields, i.e. tag/typed value pairs with possible nested messages called groups

  • Smart field creation and access, rendering field value to ival, sval or fval depending on context

  • Named Message setters/getters (name, long_name, seq, service_id, subservice_id)

Getting started

The UFEed_Java is provided as a Java project with jar packages for external dependencies: ZeroMQ, Google Protobuf, getopt. Within the target build directory of UFEed_Java we find the following directory structure (example is Linux):

ufeed_bindings_java
├── packages
│   ├── java-getopt-1.0.14.jar
│   ├── jzmq-core-3.1.1-SNAPSHOT.jar
│   ├── jzmq-devices-3.1.1-SNAPSHOT.jar
│   ├── jzmq-jni-3.1.1-SNAPSHOT.jar
│   ├── jzmq-jni-3.1.1-SNAPSHOT-native-amd64-Linux.jar
│   ├── protobuf-java-3.7.1.jar
│   ├── protobuf-java-util-3.7.1.jar
│   └── ufeedclient-19.6.0.jar
└── samples
    └── com
        └── fix8mt
            └── ufe
                └── sample
                    └── Sample.java

Interface

The main UFEed_Java interfaces/classes are UFEMessage, UFEMEssage.Builder and UFEedClient. Most of UFEMessage field setters and UFEedClient setters follow 'builder' pattern to simplify Java language constructs (i.e. setters return the reference to an object it was called from):

UFEMessage.Builder login = _uc.createMessage()
    .setLongName("login")
    .setType(st_system)
    .setServiceId(UFE_CMD_LOGIN)
    .addField(UFE_CMD, UFE_CMD_LOGIN, fl_system)
    .addField(UFE_LOGIN_ID, "webuser", fl_system)
    .addField(UFE_LOGIN_PW, "5e884898da28047151d0e56f8dc", fl_system);

UFEMessage and UFEMessage Builder

The UFEMessage class provides some wrapping logic around the internal Google Protobuf WireMessage format utilized by the UFEGW. Messages are objects with which requests are made from the UFEed_Java to the UFEGW.

UFEedClient::createMessage() factory method shall be used to create a message. UFEMessage is a final, i.e immutable just after UFEMessage::build() is called to finalize UFEMessage. UFEMessage.Builder class is for creating the message until it is finalized.

UFEMessage.Builder class:

public static class Builder {
    /**
    * ctors
    * @param wm WireMessage to copy from or null
    */
    public Builder(WireMessage wm);
    public Builder(WireMessage.Builder wmb);

    /**
    * Returns WireMessage builder
    * @return WireMessage builder
    */
    public WireMessage.Builder getWireMessageBuilder();

    /**
    * Longname
    * @return message long name
    */
    public String getLongName();
    public Builder setLongName(String longName);

    /**
    * Type
    * @return message type
    */
    public WireMessage.Type getType();
    public Builder setType(WireMessage.Type type);

    /**
    * Service id
    * @return message service id
    */
    int getServiceId();
    public Builder setServiceId(int serviceId);

    /**
    * Adds typed field to message
    * @param tag field tag
    * @param val field value
    * @param loc field location
    * @return self
    */
    public Builder addField(int tag, long val, UFEFieldLocation loc);
    public Builder addField(int tag, ByteString val, UFEFieldLocation loc);
    public Builder addField(int tag, String val, UFEFieldLocation loc);
    public Builder addField(int tag, char val, UFEFieldLocation loc);
    public Builder addField(int tag, double val, UFEFieldLocation loc);
    public Builder addField(int tag, boolean val, UFEFieldLocation loc);
    public Builder addField(int tag, Instant val, UFEFieldLocation loc);
    public Builder addField(int tag, UUID val, UFEFieldLocation loc);
    public Builder addField(int tag, Message.Status val, UFEFieldLocation loc);
    public Builder addField(int tag, Object val, UFEFieldLocation loc) throws UFEedException;
    public Builder addFields(Iterable<UFEField> fields);
    public Builder addGroup(int tag, GroupBuilderRef group, GroupTransformFunc tr, UFEFieldLocation loc);
    public Builder addGroupItem(UFEField.Builder group);

    /**
    * Builds UFEMessage when composing is complete. Message is ummutable agter the call.
    * @return Immutable composed UFEMessage
    */
    public UFEMessage build();
}

UFEMessage class:

public class UFEMessage {
    /**
    * Creates a message builder. When it's done, call builder.build() that returns UFEMessage.
    * @param wm WireMessage to copy from or null
    * @return message builder
    */
    public static Builder newBuilder(WireMessage wm);

    /**
    * Returns inner WireMessage
    * @return inner WireMessage
    */
    public WireMessage getWireMessage();

    /**
     * Returns mapped fields hash map
     * @return mapped fields hash map
     */
    public HashMap<Integer, UFEField> getFields();

    /**
     * Returns mapped groups hash map
     * @return mapped groups hash map
     */
    public HashMap<Integer, List<UFEMessage>> getGroups();

    /**
     * Finds field by given tag
     * @param tag tag to find field
     * @return found field or null
     */
    public UFEField findField(int tag);

    /**
     * Finds field value by given tag
     * @param tag tag to find field
     * @return found field or null
     */
    public Object findFieldValue(int tag);

    /**
    * Finds group by given tag
    * @param tag tag to find group
    * @return found group of null
    */
    public List<UFEMessage> findGroup(int tag) {
        return _groups.get(tag);
    }
}

UFEMessage and UFEMessage.Builder usage sample:

// logon
UFEMessage.Builder login = _uc.createMessage()
    .setLongName("login")
    .setType(st_system)
    .setServiceId(UFE_CMD_LOGIN)
    .addField(UFE_CMD, UFE_CMD_LOGIN, fl_system)
    .addField(UFE_LOGIN_ID, "webuser", fl_system)
    .addField(UFE_LOGIN_PW, "5e884898da28047151d0e56f8dc", fl_system);
// UFEedClient send methods accept builder class
UFEMessage loginMsg = login.build();
UFEField loginId = loginMsg.findField(UFE_LOGIN_ID);
Object loginPw = loginMsg.findFieldValue(UFE_LOGIN_PW);

UFEMessage and UFEMessage.Builder create a NewOrderSingle message with groups:

UFEMessage.Builder.GroupBuilderRef grp = new UFEMessage.Builder.GroupBuilderRef();
UFEMessage.Builder nos = _uc.createMessage()
    .setLongName("NewOrderSingle")
    .setType(st_fixmsg)
    .setServiceId(1)
    .setName(MsgType.NEWORDERSINGLE)
    .addField(ClOrdID.tag, "123", fl_body)
    .addField(TransactTime.tag, Instant.now(), fl_body)
    .addField(ExecInst.tag, ExecInst.ALL_OR_NONE, fl_body)
    .addField(OrdType.tag, OrdType.LIMIT, fl_body)
    .addField(Side.tag, Side.BUY, fl_body)
    .addGroup(NoAllocs.tag, grp, (builder, group) -> {
        builder.addGroupItem(group)
            .setLongName("NoAlloc")
            .setType(st_fixmsg)
            .setSeq(1)
            .addField(AllocAccount.tag, "ABC", fl_body)
            .addField(AllocQty.tag, 2, fl_body);
        builder.addGroupItem(group)
            .setLongName("NoAlloc")
            .setType(st_fixmsg)
            .setSeq(2)
            .addField(AllocAccount.tag, "CDE", fl_body)
            .addField(AllocQty.tag, 4, fl_body);
    }, fl_body);

UFEedClient

The UFEedClient class is used as the interface to make both System and Business API calls to the UFEGW. Sessions between UFEedClient and the UFEGW are made up of ZeroMQ PUB/SUB and REQ/REP sockets. The network addresses and message topics inherent to these sockets are configurable via UFEedClient. In addition, the UFEedClient manages these UFEGW sessions on behalf of the user (after the user has successfully logged in).

UFEedClient provides a callback interface called Listener that must be implemented by UFEedClient consumer:

public interface Listener {
    /**
    * Called when subscription message received
    * @param message received subscription message
    */
    void subscriptionMessageReceived(UFEMessage message);

    /**
    * Called when responder message received
    * @param message received responder message
    */
    void responderMessageReceived(UFEMessage message);

    /**
    * Called when response message received
    * @param message received response message
    */
    void responseMessageReceived(UFEMessage message);

    /**
    * Called when authentication is requested
    * @param user user to check
    * @param password user password to check
    * @return true for successful authentication, otherwise false
    */
    boolean authenticateRequested(String user, String password);

    /**
    * Called when ZeroMQ error happened
    * @param error ZMQ error code
    * @return true to continue, false to stop processing loop
    */
    boolean zeroMQErrorHappened(int error);

    /**
    * Called when error happened
    * @param error error message
    * @param exception exception happened
    * @return true to continue, false to stop processing loop
    */
    boolean errorHappened(String error, Exception exception);
}

::: :::

UFEedClient is configured with UFEConfiguration class:

public class UFEedConfiguration {
    /**
    * Subscriber endpoint, defaults to "tcp://127.0.0.1:55745"
    * @return subscriber endpoint
    */
    public String getSubscriber();
    public UFEedConfiguration setSubscriber(String subscriber);

    /**
    * Requester endpoint, defaults to "tcp://127.0.0.1:55746"
    * @return requester endpoint
    */
    public String getRequester();
    public UFEedConfiguration setRequester(String requester);

    /**
    * Publisher endpoint, defaults to "tcp://*:55747"
    * @return publisher endpoint
    */
    public String getPublisher();
    public UFEedConfiguration setPublisher(String publisher);

    /**
    * Responder endpoint, defaults to "tcp://*:55748"
    * @return responder endpoint
    */
    public String getResponder();
    public UFEedConfiguration setResponder(String responder);

    /**
    * Subscriber topic, defaults to "ufegw-publisher"
    * @return subscriber topic
    */
    public String getSubscriberTopic();
    public UFEedConfiguration setSubscriberTopic(String subscriberTopic);

    /**
    * Requester topic, defaults to "ufegw-responder"
    * @return requester topic
    */
    public String getRequesterTopic();
    public UFEedConfiguration setRequesterTopic(String requesterTopic);

    /**
    * Publisher topic, defaults to "ufeedclient-publisher"
    * @return publisher topic
    */
    public String getPublisherTopic();
    public UFEedConfiguration setPublisherTopic(String publisherTopic);

    /**
    * Responder topic, defaults to "ufeedclient-responder"
    * @return responder topic
    */
    public String getResponderTopic();
    public UFEedConfiguration setResponderTopic(String responderTopic);

    /**
    * ZMQ max IO threads
    * @return ZMQ max io threads
    */
    public int getMaxIoThreads();
    public UFEedConfiguration setMaxIoThreads(int maxIoThreads);

    /**
    * Poll interval in milliseconds
    * @return poll interval in milliseconds
    */
    public int getPollIntervalMs();
    public UFEedConfiguration setPollIntervalMs(int pollIntervalMs);
}

UFEedClient interface:

public class UFEedClient implements AutoCloseable {
    /**
    * Constructs UFEedClient
    * @param configuration configuration to use
    */
    public UFEedClient(UFEedConfiguration configuration, Listener listener);

    /**
    * Implementation for AutoClosable::close() - frees ZMQ resources
    * @throws Exception
    */
    @Override
    public void close() throws Exception;

    /**
    * UFEedClient configuration
    * @return UFEedClient configuration
    */
    public UFEedConfiguration getConfiguration();

    /**
    * Starts UFEedClient. When started in synchronous mode (wait = true)
    * it does not return until stop() is called from a different thread.
    * @param wait true for synchronous call, false for asynchronous
    */
    public void start(boolean wait);

    /**
    * Stops UFEedClient
    * @throws InterruptedException
    */
    public void stop() throws InterruptedException;

    /**
    * Creates UFEMessage
    * @param longName message long name
    * @param type message type
    * @param serviceId message service id
    * @return message builder
    */
    public UFEMessage.Builder createMessage(String longName, WireMessage.Type type, int serviceId);
    public UFEMessage.Builder createMessage(WireMessage wm);

    /**
    * Synchronously sends request to UFE and waits for UFE response
    * @param request request to send
    * @return received response
    * @throws UFEedException thrown if no session token found
    * @throws InvalidProtocolBufferException thrown if protobuf parsing failed
    */
    public UFEMessage request(UFEMessage.Builder request) throws UFEedException, InvalidProtocolBufferException;

    /**
    * Send message to responder channel
    * IMPORTANT: Must be called from responderMessageReceived callback thread as much as possible
    * @param msg message to send
    */
    public void respond(UFEMessage msg);

    /**
    * UFEedClient callback interface
    */
    public interface Listener { ... }
}

UFEedClient usage sample:

UFEedClient _uc = new UFEedClient(new UFEedConfiguration().setSubscriber(SUBSCRIBER_DEFAULT).setResponderTopic("ufegw-requester"),
  new UFEedClient.Listener() {
    @Override
    public void subscriptionMessageReceived(UFEMessage message) { ... }
    @Override
    public void responderMessageReceived(UFEMessage message) { ... }
    @Override
    public void responseMessageReceived(UFEMessage message) { ... }
    @Override
    public boolean authenticateRequested(String user, String password) { ... }
    @Override
    public boolean zeroMQErrorHappened(int error) { ... }
    @Override
    public boolean errorHappened(String error, Exception exception) { ... }
});
_uc.start(false);

// logon
UFEMessage.Builder login = _uc.createMessage("login", st_system, UFE_CMD_LOGIN)
    .addField(UFE_CMD, UFE_CMD_LOGIN, fl_system)
    .addField(UFE_LOGIN_ID, "webuser", fl_system)
    .addField(UFE_LOGIN_PW, "5e884898da28047151d0e56f8dc", fl_system);
try {
    UFEMessage response = _uc.request(login);
    if (response.findField(UFE_SESSION_TOKEN) == null)
        throw new UFEedException("Session token is missing");
    Object sessToken = response.findFieldValue(UFE_SESSION_TOKEN);
    if (sessToken == null)
        throw new UFEedException("Session token is missing #1");
    if(!(sessToken instanceof UUID))
        throw new UFEedException("Session token is of unexpected type");
    if (sessToken.toString().isEmpty())
        throw new UFEedException("Session token is empty");

    // service list request
    response = _uc.request(_uc
        .createMessage("service_list", st_system, UFE_CMD_SERVICE_LIST)
        .addField(UFE_CMD, UFE_CMD_SERVICE_LIST, fl_system));
...
} finally {
    _uc.stop();
    _uc.close();
}

Constants

The UFEed_Java maintains a list of constant values that translate to integer codes in the UFEGW. These integer codes are used to identify System API services as well as general FIX functionality. A full list of these constants is available at src/com/fix8mt/ufe/ufeedclient/Consts.java. The file with constants could be regenrated using genconsts project.

FIX variants constants

The UFEed_Java provides constants for all stock FIX variants:

import com.fix8mt.ufe.FIX50SP2.ufe_java_fields_fix50sp2.*;
...
UFEMessage.Builder nos = _uc.createMessage()
    .setLongName("NewOrderSingle")
    .setType(st_fixmsg)
    .setServiceId(1)
    .setName(MsgType.NEWORDERSINGLE)
    .addField(ClOrdID.tag, "123", fl_body)
    .addField(TransactTime.tag, Instant.now(), fl_body)
    .addField(ExecInst.tag, ExecInst.ALL_OR_NONE, fl_body)
    .addField(OrdType.tag, OrdType.LIMIT, fl_body)
    .addField(Side.tag, Side.BUY, fl_body);

Building

The UFEed_Java build follows a standard Java build pattern. Jetbrains IntelliJ project files are provided.

The UFEed_Java provides a sample to use as a starting point for UFEed Java development. The sample is under root folder. To build/run sample, you have to set Java path to UFEedClient jar and all the dependency jars and call Sample.Main():