ETA Tutorial 8 – Connecting to the Real-Time Optimized with Authentication Version 2

Download tutorial source code

Click here to download

Last update July 2024
Environment Windows, Linux
Compilers Oracle JDK 1.11 & 1.17, OpenJDK 1.11 & 1.17, Amazon Corretto 11
Prerequisite ETA Tutorial 4 - Retrieving and decoding level 1 content and RTO Version 2 Authentication credentials

Introduction​

Important Note: If you are using the Wealth solution (Pricing Streaming Optimized Service or Pricing Streaming Service), the products currently support Authentication Version 1 only (As of May 2024). Please contact your LSEG representative to verify if you are using Wealth or RTO solution.

This tutorial assumes you are familiar with consuming MarketPrice data from RTDS (Real-Time Distribution System) using ETA Java API. In this tutorial, we will modify the code in the Tutorial 4 (ETA Tutorial 4 - Retrieving and decoding level 1 content) to connect to Real-Time Optimized (RTO) using the Authentication Version 2 (aka Customer Identity and Access Management - CIAM, or Service Account) and consume MarketPrice data.

For further details about Migrating a ETA Java API application from the Authentication Version 1 to Version 2, please check out the following resource:

Description

Real-Time – Optimized is LSEG real-time content, hosted in the public cloud. It provides fast and simple access to our unparalleled content from hundreds of exchanges and OTC markets around the world. The existing consuming applications can be easily modified to connect to RTO and retrieve market data.

Prerequisites

To connect the Real-Time Optimized on Cloud, the service account (v2 authentication) is required. The service account contains:

  • A client Id: It is a username, and it is in this GE-XXXXXXXXXXXX format.
  • A client service. It is a password generated from the Platform Admin website. It is in this XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX format.

To get the service account (v2 authentication), please contact the LSEG representative directly.

You can find more information about the Authentication Version 2 from the following resources:

Steps

There are three steps to modify and configure the existing ETA Java applications to connect to RTO and retrieve market data.

1. Set a client Id and client secret

A client Id, client secret and prefer RTO region must be set in an instance of the ConsumerRole class.

First, the clientIdclientSecret, and location variables are specified in the basicConsumer class to store the Client ID, Client Secret, and RTO region respectively. Then, create a new instance of ReactorOAuthCredential class. This class represents the OAuth credential for authorization with the token service. The srvrhostname and srvrPortNo variables are removed.

    	
            

public class basicConsumer implements ConsumerCallback {

    // Supporting variables

    private Reactor reactor;

    private ReactorChannel channel;

    private ReactorOptions reactorOptions = ReactorFactory.createReactorOptions();

    private ReactorErrorInfo errorInfo = ReactorFactory.createReactorErrorInfo();

    ...

        //RTO V2

    private ReactorOAuthCredential oAuthCredential = ReactorFactory.createReactorOAuthCredential();

    private static String clientId = "<Client ID>";

    private static String clientSecret = "<Client Secret>";

    private static String location = "ap-northeast-1"; //change this location to your prefer location.

    ...

    // Consumer Role

    ConsumerRole consumerRole = ReactorFactory.createConsumerRole();

 

    // Server host name, RTDS, do not need here

    //private static final String srvrHostname = "server";

   

    // Server host name, RTDS, do not need here

    //private static final String srvrPortNo = "14002";

 

    // Service name

    private static final String serviceName = "ELEKTRON_DD";

    ...

Note: The above example uses a "ap-northeast-1" region as an example location. You can optionally change this to test other regions. Please check with your LSEG representative. To retrieve a valid list of the supported regions, refer to the Currently Deployed Sites section outlined in the Real-Time - Optimized Install and Config Guide document.

Finally, in the basicConsumer.init() method, set the clientId and clientSecret to the instance of ReactorOAuthCredential and then set the instance of ReactorOAuthCredential to the ConsumerRole. The location variable will be set later in the step #2 below.

    	
            

private void init() {

    ...

    // Initialize a default login request

    consumerRole.initDefaultRDMLoginRequest();

    // Initialize the default directory request (Use 2 as the Directory Stream Id)

    consumerRole.initDefaultRDMDirectoryRequest();

 

    // Define our RDM callback handlers

    consumerRole.defaultMsgCallback(this);

    consumerRole.channelEventCallback(this);

    consumerRole.loginMsgCallback(this);

    consumerRole.directoryMsgCallback(this);

    consumerRole.dictionaryMsgCallback(this);

 

    //RTO V2

    oAuthCredential.clientId().data(clientId);

    oAuthCredential.clientSecret().data(clientSecret);

    oAuthCredential.userSpecObj(oAuthCredential);

    consumerRole.reactorOAuthCredential(oAuthCredential);

    ...

2. Change the connection options

RTO servers are encrypted servers available on cloud in different regions. Therefore, in the basicConsumer.init() method, the connection options must be modified to use the location and encrypted socket connection type instead.

    	
            

private void init() {

    ...

    // Prepare our connection

    connectOptions.connectionList().add(connectInfo);

    connectOptions.connectionList().get(0).connectOptions().majorVersion(Codec.majorVersion());

    connectOptions.connectionList().get(0).connectOptions().minorVersion(Codec.minorVersion());

    // RTDS, do not need here

    //connectOptions.connectionList().get(0).connectOptions().connectionType(ConnectionTypes.SOCKET);

    //connectOptions.connectionList().get(0).connectOptions().unifiedNetworkInfo().address(srvrHostname);        

    //connectOptions.connectionList().get(0).connectOptions().unifiedNetworkInfo().serviceName(srvrPortNo);    

       

    // RTO V2

    connectOptions.connectionList().get(0).connectOptions().connectionType(ConnectionTypes.ENCRYPTED);

    connectOptions.connectionList().get(0).location(location);

    connectOptions.connectionList().get(0).enableSessionManagement(true);

    connectOptions.connectionList().get(0).connectOptions().encryptionOptions().connectionType(ConnectionTypes.SOCKET);

 

    connectOptions.reconnectAttemptLimit(-1); // attempt to recover forever

    connectOptions.reconnectMinDelay(1000); // 1 second minimum

    connectOptions.reconnectMaxDelay(60000); // 60 second maximum

    ...

3. Add the dependency libraries

To connect to RTO, the ETA API requires the following libraries available on the Java classpath:

  • etajConverter
  • httpcore
  • httpcore-nio
  • httpclient
  • commons-logging
  • json
  • slf4j-api
  • slf4j-jdk14
  • jose4j
  • lz4-java-1.8.0.jar

These libraries are available on Maven Central and in the RTSDK Java package. Therefore, you need to add these libraries to the project file. Otherwise, you will see the error messages when running the application like the following example.

    	
            

Exception in thread "main" java.lang.NoClassDefFoundError: com/refinitiv/eta/json/converter/ServiceNameIdConverter

        at com.refinitiv.eta.valueadd.reactor.ReactorFactory.createReactor(ReactorFactory.java:117)

        at com.refinitiv.eta.tutorials.valueadd.consumer.tutorial7.basicConsumer.init(basicConsumer.java:142)

        at com.refinitiv.eta.tutorials.valueadd.consumer.tutorial7.basicConsumer.main(basicConsumer.java:437)

Caused by: java.lang.ClassNotFoundException: com.refinitiv.eta.json.converter.ServiceNameIdConverter

        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)

        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)

        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)

        ... 3 more

 

#Or

 

Exception in thread "main" java.lang.NoClassDefFoundError: net/jpountz/lz4/LZ4Exception

        at com.refinitiv.eta.transport.RsslSocketChannel.<init>(RsslSocketChannel.java:258)

        at com.refinitiv.eta.transport.RsslSocketChannel.<init>(RsslSocketChannel.java:402)

        at com.refinitiv.eta.transport.SocketProtocol.createChannel(SocketProtocol.java:193)

        at com.refinitiv.eta.transport.SocketProtocol.<init>(SocketProtocol.java:122)

        at com.refinitiv.eta.transport.Transport.connect(Transport.java:295)

        at com.refinitiv.eta.valueadd.reactor.Reactor.connect(Reactor.java:990)

        at com.refinitiv.eta.tutorials.valueadd.consumer.tutorial7.basicConsumer.init(basicConsumer.java:177)

        at com.refinitiv.eta.tutorials.valueadd.consumer.tutorial7.basicConsumer.main(basicConsumer.java:437)

Caused by: java.lang.ClassNotFoundException: net.jpountz.lz4.LZ4Exception

        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)

        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)

        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)

        ... 8 more

Setup and Configuration

Refer to Setup and Configuration section within the ETA Tutorial 2 - Establishing a connection to a Provider prior to building and running this tutorial.

Build and run

Refer to Build and run section within the ETA Tutorial 1 - Creating a starter consumer application for instructions to successfully execute this tutorial.

Eg (Windows):

    	
            

>buildConsumer 8

>runConsumer 8

In our execution, we are downloading the dictionaries from the RTO RSSL Provider. You can see the receipt of the dictionaries; our request to subscribe to the item /LSEG.L (delay RIC) and the corresponding response.

Note: We issued a streaming request which will result in an initial refresh followed by updates showing change based on market events.

    	
            

Enterprise Transport API (ETA), Java Edition, LibraryVersionInfo

        productVersion: 3.8.0.0

        productInternalVersion: etaj3.8.0.L1.all.rrg

        productDate: N/A

basicConsumer initializing...

 

Successfully loaded local dictionaries

 

Connection up!  Registering our connection within our event loop

 

Received Login Refresh for Username: xxxx

Received serviceName: ELEKTRON_DD. State:               StateFilter:

                        ServiceState: 1

                        AcceptingRequests: 1

 

Found service of Interest: ELEKTRON_DD.  Using serviceId: 257

 

Received serviceName: PERM_SVR_SNKDRV. State:           StateFilter:

                        ServiceState: 1

                        AcceptingRequests: 1

 

Received serviceName: ERT_FD3_LF1. State:               StateFilter:

                        ServiceState: 1

                        AcceptingRequests: 1

 

Channel is ready. Subscribing to item(s): [/LSEG.L]

 

/LSEG.L

DOMAIN: MARKET_PRICE

REFRESH: State: Open/Ok/None - text: ""

        1/PROD_PERM: 9241

        2/RDNDISPLAY: 115

        3/DSPLY_NAME: LON.STK.EXCH/d

        4/RDN_EXCHID: LSE(64)

        6/TRDPRC_1: 9424.0

        7/TRDPRC_2: 9426.0

        8/TRDPRC_3: 9426.0

        9/TRDPRC_4: 9422.0

        10/TRDPRC_5: 9422.0

        11/NETCHNG_1: 54.0

        12/HIGH_1: 9456.0

        13/LOW_1: 9412.0

        14/PRCTCK_1: ?(2)

        15/CURRENCY: GBp(2008)

        16/TRADE_DATE: 12 JUL 2024

        18/TRDTIM_1: 09:41:11:000:000:000

        19/OPEN_PRC: 9414.0

        21/HST_CLOSE: 9370.0

        22/BID: 9422.0

        25/ASK: 9426.0

        28/NEWS: YYYY

        29/NEWS_TIME: 14:07:32:000:000:000

        30/BIDSIZE: 40.0

        31/ASKSIZE: 532.0

        ...

------------------------------------------------------------------------------------

DOMAIN: MARKET_PRICE

UPDATE: MULTIPLE

        25/ASK: 9426.0

        31/ASKSIZE: 507.0

        134/MID_PRICE: 9424.0

        135/MID_NET_CH: 53.0

        275/SEC_ACT_1: 9424.0

        346/ASK_TONE: <blank data>

        1021/SEQNUM: 1096935.0

        1025/QUOTIM: 09:41:42:000:000:000

        1071/COLID_2:  15(15)

        ...

------------------------------------------------------------------------------------

If the application got the Access Denied status message, please change the subscribed RICs in the itemNames variable.

    	
            Received Item StatusMsg for stream 10
State: Closed/Suspect/Not entitled - text: "Access Denied: User req to PE(74)"

For example, change the itemNames variable to another delayed RIC.

    	
            // Item name
private static final List<String> itemNames = Arrays.asList("/LSEG.L");

If an application got the HTTP Error 401 with "invalid_client" and "Invalid client or client credentials." error message, it means you have set invalid Client ID or Client Secret. Please re-check your input on the basicConsumer class file and contact your LSEG representative to verify your credential.

    	
            Reactor.connect failed with return code: -1 error = Failed to request authentication token information with HTTP error 401. Text: {"error":"invalid_client"  ,"error_description":"Invalid client or client credentials." }
        
        
    

References

For more information, refer to the ETA Java Development Guides.