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 |
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:
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.
To connect the Real-Time Optimized on Cloud, the service account (v2 authentication) is required. The service account contains:
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:
There are three steps to modify and configure the existing ETA Java applications to connect to RTO and retrieve market data.
A client Id, client secret and prefer RTO region must be set in an instance of the ConsumerRole class.
First, the clientId, clientSecret, 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);
...
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
...
To connect to RTO, the ETA API requires the following libraries available on the Java classpath:
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
Refer to Setup and Configuration section within the ETA Tutorial 2 - Establishing a connection to a Provider prior to building and running this tutorial.
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." }
For more information, refer to the ETA Java Development Guides.