├── .gitignore
├── .vscode
├── settings.json
└── launch.json
├── images
├── Chain.png
├── main.png
├── connect.png
├── invalid.png
├── fieldList.png
├── connectRTOv1.png
├── connectRTOv2.png
└── jarExtension.png
├── src
└── main
│ ├── assembly
│ ├── SplashScreen.xcf
│ └── distribution.xml
│ ├── distribution
│ ├── keystore.jks
│ └── README.txt
│ ├── resources
│ ├── LSEG Icon.ico
│ ├── SpeedGuide.bmp
│ ├── SpeedGuide.png
│ └── LSEG small logo.png
│ └── java
│ └── com
│ └── lseg
│ └── ema
│ └── example
│ └── gui
│ ├── view
│ ├── images
│ │ ├── mbri-home.png
│ │ ├── mbri-left.png
│ │ └── mbri-right.png
│ ├── application.css
│ ├── SpeedGuideView.fxml
│ ├── ConnectionDialog.fxml
│ ├── SpeedGuideConnection.java
│ └── SpeedGuideViewController.java
│ ├── StatusLogHandler.java
│ ├── SpeedGuide.java
│ ├── ConsumerClient.java
│ └── SpeedGuideConsumer.java
├── LICENSE.md
├── pom.xml
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /src/main/resources/images
3 | /src/main/resources/view
4 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "java.configuration.updateBuildConfiguration": "interactive"
3 | }
--------------------------------------------------------------------------------
/images/Chain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LSEG-API-Samples/Example.EMA.Java.SpeedGuide/HEAD/images/Chain.png
--------------------------------------------------------------------------------
/images/main.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LSEG-API-Samples/Example.EMA.Java.SpeedGuide/HEAD/images/main.png
--------------------------------------------------------------------------------
/images/connect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LSEG-API-Samples/Example.EMA.Java.SpeedGuide/HEAD/images/connect.png
--------------------------------------------------------------------------------
/images/invalid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LSEG-API-Samples/Example.EMA.Java.SpeedGuide/HEAD/images/invalid.png
--------------------------------------------------------------------------------
/images/fieldList.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LSEG-API-Samples/Example.EMA.Java.SpeedGuide/HEAD/images/fieldList.png
--------------------------------------------------------------------------------
/images/connectRTOv1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LSEG-API-Samples/Example.EMA.Java.SpeedGuide/HEAD/images/connectRTOv1.png
--------------------------------------------------------------------------------
/images/connectRTOv2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LSEG-API-Samples/Example.EMA.Java.SpeedGuide/HEAD/images/connectRTOv2.png
--------------------------------------------------------------------------------
/images/jarExtension.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LSEG-API-Samples/Example.EMA.Java.SpeedGuide/HEAD/images/jarExtension.png
--------------------------------------------------------------------------------
/src/main/assembly/SplashScreen.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LSEG-API-Samples/Example.EMA.Java.SpeedGuide/HEAD/src/main/assembly/SplashScreen.xcf
--------------------------------------------------------------------------------
/src/main/distribution/keystore.jks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LSEG-API-Samples/Example.EMA.Java.SpeedGuide/HEAD/src/main/distribution/keystore.jks
--------------------------------------------------------------------------------
/src/main/resources/LSEG Icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LSEG-API-Samples/Example.EMA.Java.SpeedGuide/HEAD/src/main/resources/LSEG Icon.ico
--------------------------------------------------------------------------------
/src/main/resources/SpeedGuide.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LSEG-API-Samples/Example.EMA.Java.SpeedGuide/HEAD/src/main/resources/SpeedGuide.bmp
--------------------------------------------------------------------------------
/src/main/resources/SpeedGuide.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LSEG-API-Samples/Example.EMA.Java.SpeedGuide/HEAD/src/main/resources/SpeedGuide.png
--------------------------------------------------------------------------------
/src/main/resources/LSEG small logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LSEG-API-Samples/Example.EMA.Java.SpeedGuide/HEAD/src/main/resources/LSEG small logo.png
--------------------------------------------------------------------------------
/src/main/java/com/lseg/ema/example/gui/view/images/mbri-home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LSEG-API-Samples/Example.EMA.Java.SpeedGuide/HEAD/src/main/java/com/lseg/ema/example/gui/view/images/mbri-home.png
--------------------------------------------------------------------------------
/src/main/java/com/lseg/ema/example/gui/view/images/mbri-left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LSEG-API-Samples/Example.EMA.Java.SpeedGuide/HEAD/src/main/java/com/lseg/ema/example/gui/view/images/mbri-left.png
--------------------------------------------------------------------------------
/src/main/java/com/lseg/ema/example/gui/view/images/mbri-right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LSEG-API-Samples/Example.EMA.Java.SpeedGuide/HEAD/src/main/java/com/lseg/ema/example/gui/view/images/mbri-right.png
--------------------------------------------------------------------------------
/src/main/java/com/lseg/ema/example/gui/view/application.css:
--------------------------------------------------------------------------------
1 | /* SpeedGuide CSS */
2 |
3 | /*
4 | * Service ComboBox
5 | */
6 | .combo-box .list.cell{
7 | -fx-font-size: 12px "System";
8 | }
9 |
10 | .tab-pane *.tab-header-background {
11 | -fx-background-color: #0f000d12;
12 | }
13 |
14 | /*
15 | * Status Pane
16 | */
17 | #status-time {
18 | -fx-fill: blue;
19 | -fx-font-weight:bold;
20 | }
21 |
22 | #status-request {
23 | -fx-fill: black;
24 | -fx-font-weight: normal;
25 | }
26 |
27 | #status-response-success {
28 | -fx-fill: darkgreen;
29 | -fx-font-weight: normal;
30 | }
31 |
32 | #status-response-error {
33 | -fx-fill: crimson;
34 | -fx-font-weight: normal;
35 | }
36 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright 2017 THOMSON REUTERS
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/src/main/assembly/distribution.xml:
--------------------------------------------------------------------------------
1 |
4 | distribution
5 |
6 | zip
7 |
8 |
9 |
10 | ${project.build.directory}
11 | /
12 |
13 | ${project.artifactId}.jar
14 | ${project.artifactId}.exe
15 |
16 |
17 |
18 | ${project.basedir}/src/main/distribution
19 | /
20 |
21 | *.jks
22 | *.txt
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "java",
9 | "name": "Debug (No args)",
10 | "request": "launch",
11 | "mainClass": "com.lseg.ema.example.gui.SpeedGuide",
12 | "vmArgs": "-splash:src/main/resources/SpeedGuide.png",
13 | "args": "--keyStore=src/main/distribution/keystore.jks"
14 | },
15 | {
16 | "type": "java",
17 | "name": "Debug (ADS)",
18 | "request": "launch",
19 | "mainClass": "com.lseg.ema.example.gui.SpeedGuide",
20 | "vmArgs": "-splash:src/main/resources/SpeedGuide.png",
21 | "args": "--host=${env:RTDS_HOST}"
22 | },
23 | {
24 | "type": "java",
25 | "name": "Debug (RTO V1)",
26 | "request": "launch",
27 | "mainClass": "com.lseg.ema.example.gui.SpeedGuide",
28 | "vmArgs": "-splash:src/main/resources/SpeedGuide.png",
29 | "args": ["--keyStore=src/main/distribution/keystore.jks", "--machineId=${env:MACHINE_ID}",
30 | "--password=${env:MACHINE_PASSWD}", "--appKey=${env:APPKEY}", "--region=eu-west-1"]
31 | },
32 | {
33 | "type": "java",
34 | "name": "Debug (RTO V2)",
35 | "request": "launch",
36 | "mainClass": "com.lseg.ema.example.gui.SpeedGuide",
37 | "vmArgs": "-splash:src/main/resources/SpeedGuide.png",
38 | "args": ["--keyStore=src/main/distribution/keystore.jks", "--clientId=${env:CLIENT_ID}",
39 | "--clientSecret=${env:CLIENT_SECRET}", "--region=ap-northeast-1"]
40 | },
41 | {
42 | "type": "java",
43 | "name": "Debug (debug logging)",
44 | "request": "launch",
45 | "mainClass": "com.lseg.ema.example.gui.SpeedGuide",
46 | "vmArgs": "-splash:src/main/resources/SpeedGuide.png",
47 | "args": ["--keyStore=src/main/distribution/keystore.jks", "--d"]
48 | },
49 | ]
50 | }
--------------------------------------------------------------------------------
/src/main/java/com/lseg/ema/example/gui/StatusLogHandler.java:
--------------------------------------------------------------------------------
1 | package com.lseg.ema.example.gui;
2 |
3 | import java.util.logging.Level;
4 | import java.util.logging.LogRecord;
5 | import java.util.logging.StreamHandler;
6 |
7 | import com.lseg.ema.example.gui.SpeedGuide.StatusIndicator;
8 | import com.lseg.ema.example.gui.view.SpeedGuideViewController;
9 |
10 | public class StatusLogHandler extends StreamHandler {
11 | private SpeedGuideViewController m_viewController;
12 |
13 | public void setViewController(SpeedGuideViewController viewController) {
14 | m_viewController = viewController;
15 | }
16 |
17 | @Override
18 | public void publish(LogRecord record)
19 | {
20 | if ( m_viewController != null && (record.getLevel() == Level.WARNING || record.getLevel() == Level.SEVERE) )
21 | {
22 | final String TOKEN_TEXT = "Text:";
23 | final String TOKEN_DETAILS = "Error text";
24 | final String TOKEN_STATE = "State:";
25 |
26 | // Ignore the error reporting no EmaConfig.xml found
27 | if ( !record.getMessage().contains("EmaConfig.xml"))
28 | {
29 | String[] lines = record.getMessage().split("\n");
30 |
31 | String text = "";
32 | for (String line : lines)
33 | {
34 | if (line.contains(TOKEN_TEXT))
35 | text += line.substring(line.indexOf(TOKEN_TEXT)+TOKEN_TEXT.length()).trim() + SpeedGuide.NEWLINE;
36 | else if (line.contains(TOKEN_DETAILS))
37 | text += line.substring(line.indexOf(TOKEN_DETAILS)+TOKEN_DETAILS.length()).trim() + SpeedGuide.NEWLINE;
38 | else if (line.contains(TOKEN_STATE))
39 | text += line.substring(line.indexOf(TOKEN_STATE)+TOKEN_STATE.length()).trim() + SpeedGuide.NEWLINE;
40 | }
41 |
42 | // Send errors to GUI status
43 | if (!text.isEmpty() ) {
44 | int pos = text.lastIndexOf(SpeedGuide.NEWLINE);
45 | if ( pos >= 0)
46 | text = text.substring(0, pos);
47 |
48 | m_viewController.updateStatus(text, StatusIndicator.RESPONSE_ERROR);
49 | }
50 | else
51 | m_viewController.updateStatus(record.getMessage(), StatusIndicator.RESPONSE_ERROR);
52 | }
53 | }
54 | super.publish(record);
55 | }
56 |
57 |
58 | @Override
59 | public void flush() {
60 | super.flush();
61 | }
62 |
63 | @Override
64 | public void close() throws SecurityException {
65 | super.close();
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/main/distribution/README.txt:
--------------------------------------------------------------------------------
1 | README
2 |
3 | The SpeedGuide tool is a simple, GUI-based, market data consumer written in Java, using the LSEG Real-Time Java SDK,
4 | that allows the ability to navigate through realtime data offered within LSEG.
5 |
6 | Refer to GitHub (https://github.com/LSEG-API-Samples/Example.EMA.Java.SpeedGuide) for full details of the operation of the utility.
7 |
8 | Note: While the utility will launch the "REFINITIV" RIC, which represents the root code of the Speed Guide pages, the utility
9 | is extremely useful for developers to visualize any other instrument available within real-time platform.
10 |
11 | The user is presented with a basic window allowing the selection of navigation items, as identified by values within <> brackets,
12 | or to input items manually.
13 |
14 | The package includes 2 components offering multiple ways to launch the tool. Packaged are:
15 |
16 | - SpeedGuide.jar: An executable JAR available for both Windows and Linux
17 | - SpeedGuide.exe: A windows wrapper
18 |
19 | Launching the tool by double clicking on the icon (Windows):
20 | ============================================================
21 |
22 | Double-clicking either the .jar or .exe file will not pass any required parameters to the application. As such, the application
23 | will present a Connection Dialog requesting for these connection parameters. In either case, no console is involved thus no
24 | additional messages, such as log messages, can be viewed.
25 |
26 | Note: Launching the executable JAR requires the Javaw (https://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html)
27 | program to open it. When not associated, you will be presented with a request to select a program.
28 |
29 |
30 | Launching the tool from the console:
31 | ====================================
32 |
33 | At the console, you can pass command-line parameters to the utility:
34 |
35 | o Launching the executable JAR
36 | > java -jar SpeedGuide.jar [options]
37 |
38 | When launching the executable JAR, users optionally specify command-line options and have the opportunity to see
39 | the output on the console.
40 |
41 | o Launching the windows wrapper EXE
42 | > SpeedGuide.exe [options]
43 |
44 | The windows wrapper is strictly a GUI based facility that does not have an explicit console attached. Thus, no
45 | output can be viewed on the console. However, users can capture the output within a file. for example:
46 |
47 | > SpeedGuide.exe>output.txt [options]
48 |
49 | Console output will provide additional log messages, and if the user specifies the --d[ebug] option on the command-line,
50 | additional debug details. These messages can be useful to better understand connection details, messages returned, etc.
51 |
52 | Command-line Options
53 | --------------------
54 |
55 | Options:
56 |
57 | --service=serviceName Optional. Service Name providing market data content.
58 | Eg: ELEKTRON_DD. Default: Determined from Directory response.
59 |
60 | ************* ADS Connection Parameters **************
61 | --host=hostname:port Required. Elektron Server address/hostname and port of your Market Data server.
62 | Syntax: :. Eg: myserver:14002 or 192.168.1.1:14002
63 | --user=userName Optional. DACS User name required if authentication is enabled on server.
64 | Note: if no user name is provided, the utility will use your desktop login
65 | --appid=ApplicationId Optional. DACS Application ID if authentication is enabled on server.
66 | Application ID has no default.
67 | --position=Position Optional. DACS Position if authentication is enabled on server.
68 | Position has no default.
69 |
70 | ************* Real-Time -- Optimized Connection Parameters **************
71 | --region=location Optional. Specify the location to connect within the cloud.
72 | Eg: ap-northeast-1 (Asia) eu-west-1 (EU) us-east-2 (US). Default: us-east-1
73 |
74 | **** Version 1 Authentication >
75 | --machineId=machine ID Required. Real-Time -- Optimized Machine ID/User required for OAuth Password Grant.
76 | Eg: GE-A-00000000-1-8888
77 | --password=password Required. Real-Time -- OPtimized password required for OAuth Password Grant.
78 | Eg: Sunshine_1_UserPass
79 | --appKey=App Key Required. Real-Time -- Optimized AppKey or Client ID required for server authentication.
80 | Eg: x888x8x88888888x88888x88x8888xx88x88888x
81 |
82 | **** Version 2 Authentication >
83 | --clientId=Client ID Required. Real-Time -- Optimized Client/Service Account ID required for OAuth Client Credentials.
84 | Eg: GE-XXXXXXXXXXXX
85 | --clientSecret=secret Required. Real-Time -- Optimized Client secret required for OAuth Client Credentials.
86 | Eg: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
87 |
88 |
89 | --keyStore=keystorefile Optional. A Java KeyStore (JKS) required for secure package exchange.
90 | Default: SpeedGuide provides a file for convenience.
91 | --keyStorePasswd=passwd Optional. Password associated with the specified keystore file.
92 | Default: SpeedGuide includes the password for the default keystore file.
93 |
94 | --d[ebug] Debug Mode. Display verbose messages to the console
95 | --h[elp] Prints this screen
96 |
97 | If neither the required parameters for the ADS or Real-Time -- Optmized are specified, the utility will prompt the user.
98 |
99 | Example:
100 | > SpeedGuide.exe --host=myserver:14002 --service=ELEKTRON_AD --user=testuser --appid=256 --position=127.0.0.1
101 | > SpeedGuide.exe --clientId=GE-XXXX1234XXXX --clientSecret=9x999999-9xxx-9999-9x99-9x9xx99x9x99
102 | > SpeedGuide.exe --clientId=GE-123X9ABCDE9Z --clientSecret=9z123456-9abc-5555-9a12-1a2bc34d5e67 --region=eu-west-1
103 |
104 |
--------------------------------------------------------------------------------
/src/main/java/com/lseg/ema/example/gui/view/SpeedGuideView.fxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
51 |
52 |
53 |
62 |
71 |
80 |
81 |
82 |
83 |
84 |
85 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.lseg.ema.example.gui
8 | SpeedGuide
9 | 4.1.0
10 |
11 | London Stock Exchange
12 |
13 | 2017
14 | Tool to navigate LSEG SpeedGuide market data
15 |
16 |
17 | 8
18 | 8
19 | UTF-8
20 |
21 |
22 |
23 |
24 | com.refinitiv.ema
25 | ema
26 | 3.7.2.0
27 |
28 |
29 |
30 | com.refinitiv.eta
31 | eta
32 | 3.7.2.0
33 |
34 |
35 |
36 |
37 | ${project.artifactId}
38 |
39 |
40 | maven-resources-plugin
41 | 3.3.1
42 |
43 |
44 |
45 | copy-view-resources
46 | generate-resources
47 |
48 | copy-resources
49 |
50 |
51 | ${project.basedir}/src/main/resources/view
52 |
53 |
54 | src/main/java/com/lseg/ema/example/gui/view
55 |
56 | **/*.fxml
57 | **/*.css
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | copy-image-resources
67 | generate-resources
68 |
69 | copy-resources
70 |
71 |
72 | ${project.basedir}/src/main/resources/view/images
73 |
74 |
75 | src/main/java/com/lseg/ema/example/gui/view/images
76 |
77 | **/*.png
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | org.apache.maven.plugins
88 | maven-jar-plugin
89 | 3.3.0
90 |
91 |
92 |
93 | true
94 | lib/
95 | com.lseg.ema.example.gui.SpeedGuide
96 | true
97 |
98 |
99 | SpeedGuide.png
100 |
101 |
102 |
103 |
104 |
105 | org.apache.maven.plugins
106 | maven-shade-plugin
107 | 3.5.1
108 |
109 | false
110 |
111 |
112 |
113 | package
114 |
115 | shade
116 |
117 |
118 |
119 |
120 |
121 | com.lseg.ema.example.gui.SpeedGuide
122 | 1.0
123 |
124 |
125 |
126 |
127 |
128 | *:*
129 |
130 | module-info.class
131 | META-INF/versions/9/module-info.class
132 | META-INF/*.SF
133 | META-INF/*.DSA
134 | META-INF/*.RSA
135 | META-INF/LICENSE*
136 | META-INF/NOTICE*
137 | META-INF/DEPENDENCIES
138 | META-INF/MANIFEST.MF
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 | com.akathist.maven.plugins.launch4j
148 | launch4j-maven-plugin
149 | 2.4.1
150 |
151 |
152 | l4j-clui
153 | package
154 |
155 | launch4j
156 |
157 |
158 | gui
159 | ${project.build.directory}/${project.artifactId}.jar
160 | ${project.build.directory}/${project.artifactId}.exe
161 |
162 |
163 | com.lseg.ema.example.gui.SpeedGuide
164 |
165 |
169 |
170 | C:\Program Files\Common Files\Oracle\javapath
171 | 1.8.0
172 |
173 |
174 | ${project.basedir}/src/main/resources/SpeedGuide.bmp
175 | true
176 | 10
177 | true
178 |
179 | src/main/resources/LSEG Icon.ico
180 |
181 |
182 |
183 |
184 |
185 | maven-assembly-plugin
186 | 3.6.0
187 |
188 |
189 | make-assembly
190 | package
191 |
192 | single
193 |
194 |
195 | false
196 |
197 | ${project.basedir}/src/main/assembly/distribution.xml
198 |
199 | ${project.artifactId}
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
--------------------------------------------------------------------------------
/src/main/java/com/lseg/ema/example/gui/SpeedGuide.java:
--------------------------------------------------------------------------------
1 | package com.lseg.ema.example.gui;
2 |
3 | import java.awt.AlphaComposite;
4 | import java.awt.Color;
5 | import java.awt.Graphics2D;
6 | import java.awt.SplashScreen;
7 | import java.io.InputStream;
8 | import java.net.URL;
9 |
10 | import com.lseg.ema.example.gui.view.SpeedGuideConnection;
11 | import com.lseg.ema.example.gui.view.SpeedGuideViewController;
12 |
13 | import javafx.application.Application;
14 | import javafx.fxml.FXMLLoader;
15 | import javafx.scene.Scene;
16 | import javafx.scene.image.Image;
17 | import javafx.scene.layout.AnchorPane;
18 | import javafx.stage.Stage;
19 |
20 |
21 | // SpeedGuide
22 | //
23 | // Main class driving the application. The SpeedGuide class represents our entry point for our UI
24 | // and launching the EMA Consumer thread to manage all market data activity. The SpeedGuide utilizes
25 | // the FXML specification to drive the UI components. All FXML definitions are launched at startup within
26 | // this class.
27 | public class SpeedGuide extends Application
28 | {
29 | public enum StatusIndicator
30 | {
31 | REQUEST,
32 | RESPONSE_SUCCESS,
33 | RESPONSE_ERROR
34 | }
35 |
36 | public static final String NEWLINE = System.getProperty("line.separator");
37 | public static final String VER_CODE = "4.1.0";
38 | public SpeedGuideConsumer m_consumer = new SpeedGuideConsumer();
39 | private boolean m_debug = false;
40 |
41 | @Override
42 | public void start(Stage primaryStage) throws Exception {
43 | try {
44 | final SplashScreen splash = SplashScreen.getSplashScreen();
45 | if (splash != null) {
46 | Graphics2D g = splash.createGraphics();
47 | if (g == null) {
48 | System.out.println("g is null");
49 | return;
50 | }
51 | // render the splash screen
52 | renderSplashFrame(g, (int)splash.getSize().getHeight());
53 | splash.update();
54 | }
55 |
56 | // Determine if we passed anything on the cmd line
57 | parseCmdLine();
58 |
59 | // Define our controllers
60 | SpeedGuideViewController viewController = loadFXMLController("/view/SpeedGuideView.fxml");
61 | SpeedGuideConnection connectionController = loadFXMLController("/view/ConnectionDialog.fxml");
62 |
63 | appInit(primaryStage, viewController, connectionController);
64 |
65 | if ( splash != null )
66 | splash.close();
67 |
68 | // Attempt to Connect into the configured server (ADS or Refinitiv Real-Time -- Optimized)
69 | connectionController.connect();
70 | } catch (Exception e) {
71 | System.out.print("Exception in Application Start: ");
72 | e.printStackTrace();
73 | stop();
74 | }
75 | }
76 |
77 | private static void renderSplashFrame(Graphics2D g, int height) {
78 | g.setComposite(AlphaComposite.Clear);
79 | g.fillRect(120,140,200,40);
80 | g.setPaintMode();
81 | g.setColor(Color.BLACK);
82 |
83 | // Set the position for the string. Adjust these values as needed.
84 | int xPosition = 10; // 10 pixels from the left edge of the window
85 | int yPosition = height - 10; // 10 pixels from the bottom edge of the window
86 |
87 | g.drawString("Loading...", xPosition, yPosition);
88 | }
89 |
90 | private void appInit(Stage primaryStage, SpeedGuideViewController viewController, SpeedGuideConnection connectionController)
91 | {
92 | // Wire up our Model/View/Controllers
93 | viewController.setConnectionViewController(connectionController);
94 | viewController.setDebug(m_debug);
95 | viewController.defineControlBindings(m_consumer);
96 |
97 | // Notify our consumer some setup information
98 | m_consumer.setDebug(m_debug);
99 | m_consumer.setViewController(viewController);
100 |
101 | // Define the main viewing scene,
102 | AnchorPane layout = viewController.getLayout();
103 | Scene scene = new Scene(layout, layout.getPrefWidth(), layout.getPrefHeight());
104 |
105 | // Prevent the user from resizing the window too small
106 | primaryStage.setMinHeight(layout.getMinHeight());
107 | primaryStage.setMinWidth(layout.getMinWidth());
108 |
109 | // Assign to our main stage and show the application to the end user
110 | primaryStage.setTitle("Speed Guide");
111 | InputStream iconStream = getClass().getResourceAsStream("/LSEG small logo.png");
112 | if (iconStream != null)
113 | primaryStage.getIcons().add(new Image(iconStream));
114 | primaryStage.setScene(scene);
115 | primaryStage.show();
116 |
117 | // Set up our EMA Consumer and launch a thread to run...
118 | Thread t = new Thread(m_consumer);
119 | t.start();
120 |
121 | // Initialize Connection Parameters
122 | connectionController.initialize(getParameters(), m_consumer);
123 | }
124 |
125 | private T loadFXMLController(String resource)
126 | {
127 | try {
128 | // Load our view (from an fxml layout)
129 | FXMLLoader loader = new FXMLLoader();
130 | URL fxmlResource = getClass().getResource(resource);
131 | loader.setLocation(fxmlResource);
132 | loader.load();
133 | return(loader.getController());
134 | } catch (Exception e) {
135 | System.out.print("Exception in loading FXML resource: " + resource + ": ");
136 | e.printStackTrace();
137 | stop();
138 | }
139 | return null;
140 | }
141 |
142 | private void parseCmdLine() throws Exception
143 | {
144 | Application.Parameters params = getParameters();
145 |
146 | if ( params.getRaw().contains("--h") || params.getRaw().contains("--help") ||
147 | params.getNamed().containsKey("h") || params.getNamed().containsKey("help"))
148 | {
149 | System.out.println(NEWLINE+"Syntax:"+NEWLINE);
150 | System.out.println(" > java -jar SpeedGuide.jar [options] or"+NEWLINE);
151 | System.out.println(" > SpeedGuide.exe [options] "+NEWLINE);
152 | System.out.println("Options:"+NEWLINE);
153 | System.out.println(" --service=serviceName Optional. Service Name providing market data content.");
154 | System.out.println(" Eg: ELEKTRON_DD. Default: Determined from Directory response.");
155 | System.out.println(NEWLINE);
156 | System.out.println("************* ADS Connection Parameters **************");
157 | System.out.println(" --host=hostname:port Required. ADS Server address/hostname and port of your Real-Time Distribution Server.");
158 | System.out.println(" Syntax: :. Eg: myserver:14002 or 192.168.1.1:14002");
159 | System.out.println(" --user=userName Optional. DACS User name required if authentication is enabled on server.");
160 | System.out.println(" Note: if no user name is provided, the utility will use your desktop login");
161 | System.out.println(" --appid=ApplicationId Optional. DACS Application ID if authentication is enabled on server.");
162 | System.out.println(" Application ID has no default.");
163 | System.out.println(" --position=Position Optional. DACS Position if authentication is enabled on server.");
164 | System.out.println(" Position has no default.");
165 | System.out.println(NEWLINE);
166 | System.out.println("************* Refinitiv Real-Time -- Optimized Connection Parameters **************");
167 | System.out.println(" --region=location Optional. Specify the location to connect within the cloud.");
168 | System.out.println(" Eg: ap-northeast-1 (Asia) eu-west-1 (EU) us-east-2 (US). Default: us-east-1");
169 | System.out.println(NEWLINE);
170 | System.out.println("**** Version 1 Authentication >");
171 | System.out.println(" --machineId=machine ID Required. Real-Time -- Optimized Machine ID/User required for OAuth Password Grant.");
172 | System.out.println(" Eg: GE-A-00000000-1-8888");
173 | System.out.println(" --password=password Required. Real-Time -- OPtimized password required for OAuth Password Grant.");
174 | System.out.println(" Eg: Sunshine_1_UserPass");
175 | System.out.println(" --appKey=App Key Required. Refinitiv Real-Time -- Optimized AppKey or Client ID required for server authentication.");
176 | System.out.println(" Eg: x888x8x88888888x88888x88x8888xx88x88888x");
177 | System.out.println("**** Version 2 Authentication >");
178 | System.out.println(" --clientId=Client ID Required. Real-Time -- Optimized Client/Service Account ID required for OAuth Client Credentials.");
179 | System.out.println(" Eg: GE-XXXXXXXXXXXX");
180 | System.out.println(" --clientSecret=secret Required. Real-Time -- Optimized Client secret required for OAuth Client Credentials.");
181 | System.out.println(" Eg: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\n");
182 | System.out.println(" --keyStore=keystorefile Optional. A Java KeyStore (JKS) required for secure package exchange.");
183 | System.out.println(" Default: SpeedGuide provides a file for convenience.");
184 | System.out.println(" --keyStorePasswd=passwd Optional. Password associated with the specified keystore file.");
185 | System.out.println(" Default: SpeedGuide includes the password for the default keystore file.");
186 | System.out.println(NEWLINE);
187 | System.out.println(" --d[ebug] Debug Mode. Display verbose messages to the console");
188 | System.out.println(" --h[elp] Prints this screen"+NEWLINE);
189 | System.out.println("If neither the required parameters for the ADS or Refinitiv Real-Time -- Optimized parameters are specified, the utility will prompt the user.");
190 | System.out.println(NEWLINE);
191 | System.out.println("Example:");
192 | System.out.println(" > SpeedGuide.exe --host=myserver:14002 --service=ELEKTRON_DD --user=testuser --appid=256 --position=127.0.0.1");
193 | System.out.println(" > SpeedGuide.exe --machineId=GE-A-00000000-1-8888 --password=Sunshine_1_UserPass --appKey=x888x8x88888888x88888x88x8888xx88x88888x");
194 | System.out.println(" > SpeedGuide.exe --clientId=GE-123X9ABCDE9Z --clientSecret=9z123456-9abc-5555-9a12-1a2bc34d5e67 --region=eu-west-1");
195 | stop();
196 | }
197 |
198 | m_debug = params.getRaw().contains("--d") || params.getRaw().contains("--debug") ||
199 | params.getNamed().containsKey("d") || params.getNamed().containsKey("debug");
200 | }
201 |
202 | @Override
203 | public void stop() {
204 | System.exit(0);
205 | }
206 |
207 | public static void main(String[] args) throws Exception {
208 | System.out.println("Java Version: " + System.getProperty("java.runtime.version") + " (" + System.getProperty("sun.arch.data.model") + "-bit)");
209 | System.out.println("javafx.runtime.version: " + System.getProperties().get("javafx.runtime.version"));
210 |
211 | launch(args);
212 | }
213 | }
214 |
--------------------------------------------------------------------------------
/src/main/java/com/lseg/ema/example/gui/view/ConnectionDialog.fxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
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 |
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 |
103 |
104 |
105 |
106 |
107 |
108 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
--------------------------------------------------------------------------------
/src/main/java/com/lseg/ema/example/gui/view/SpeedGuideConnection.java:
--------------------------------------------------------------------------------
1 | package com.lseg.ema.example.gui.view;
2 |
3 | import com.lseg.ema.example.gui.SpeedGuideConsumer;
4 |
5 | import javafx.application.Application;
6 | import javafx.beans.binding.Bindings;
7 | import javafx.fxml.FXML;
8 | import javafx.scene.control.Button;
9 | import javafx.scene.control.SingleSelectionModel;
10 | import javafx.scene.control.Tab;
11 | import javafx.scene.control.TabPane;
12 | import javafx.scene.control.TextField;
13 | import javafx.scene.input.KeyCode;
14 | import javafx.scene.input.KeyEvent;
15 | import javafx.scene.Scene;
16 | import javafx.stage.Modality;
17 | import javafx.stage.Stage;
18 | import javafx.stage.StageStyle;
19 |
20 | // SpeedGuideConnection
21 | //
22 | // Connection View Controller for connection dialog. This view controller is created within the
23 | // SpeedGuide.java class upon loading of the associated FXML UI components. The loading references
24 | // this definition and thus automatically creates an instance of the view controller.
25 | //
26 | public class SpeedGuideConnection
27 | {
28 | // Real-Time -- Optimized commandline parameters
29 | private static final String SERVICE_PARAM = "service";
30 |
31 | public class ADSConnection
32 | {
33 | // Command-line parameter names
34 | private static final String HOST_PARAM = "host";
35 | private static final String USER_PARAM = "user";
36 | private static final String APPID_PARAM = "appid";
37 | private static final String POSITION_PARAM = "position";
38 |
39 | public void setConnection(Application.Parameters cmdLineParams)
40 | {
41 | setConnection(cmdLineParams.getNamed().get(HOST_PARAM),
42 | cmdLineParams.getNamed().get(SERVICE_PARAM),
43 | cmdLineParams.getNamed().get(USER_PARAM),
44 | cmdLineParams.getNamed().get(APPID_PARAM),
45 | cmdLineParams.getNamed().get(POSITION_PARAM));
46 | }
47 |
48 | public void setConnection(String host, String service, String user, String appId, String position)
49 | {
50 | if ( host != null) _host = host.trim();
51 | if ( service != null) _service = service.trim();
52 | if ( user != null) _user = user.trim();
53 | if ( appId != null) _appId = appId.trim();
54 | if ( position != null) _position = position.trim();
55 | }
56 |
57 | public String _host = "";
58 | public String _service = "";
59 | public String _user = "";
60 | public String _appId = "";
61 | public String _position = "";
62 | }
63 |
64 | public class RTO_V1Connection
65 | {
66 | // *********************************
67 | // Command-line parameter names
68 | // *********************************
69 |
70 | // V1 (Grant Password)
71 | private static final String MACHINEID_PARAM = "machineId";
72 | private static final String PASSWORD_PARAM = "password";
73 | private static final String APPKEY_PARAM = "appKey";
74 |
75 | private static final String KEYSTORE_PARAM = "keyStore";
76 | private static final String KEYSTOREPASSWD_PARAM = "keyStorePasswd";
77 |
78 | public void setConnection(Application.Parameters cmdLineParams)
79 | {
80 | setConnection(cmdLineParams.getNamed().get(MACHINEID_PARAM),
81 | cmdLineParams.getNamed().get(PASSWORD_PARAM),
82 | cmdLineParams.getNamed().get(APPKEY_PARAM),
83 | cmdLineParams.getNamed().get(SERVICE_PARAM));
84 |
85 | String value = cmdLineParams.getNamed().get(KEYSTORE_PARAM);
86 | if (value != null) _keystoreFile = value.trim();
87 |
88 | value = cmdLineParams.getNamed().get(KEYSTOREPASSWD_PARAM);
89 | if (value != null) _keystorePasswd = value.trim();
90 | }
91 |
92 | public void setConnection(String machineId, String password, String appKey, String service)
93 | {
94 | if (machineId != null) _machineId = machineId.trim();
95 | if (password != null) _password = password.trim();
96 | if (appKey != null) _appKey = appKey.trim();
97 | if (service != null) _service = service.trim();
98 | }
99 |
100 | public String _machineId = "";
101 | public String _password = "";
102 | public String _appKey = "";
103 | public String _service = "";
104 | public String _keystoreFile = "keystore.jks"; // Default location within the package
105 | public String _keystorePasswd = "Welcome1";
106 | }
107 |
108 | public class RTO_V2Connection
109 | {
110 | // *********************************
111 | // Command-line parameter names
112 | // *********************************
113 |
114 | // V2 (Client Credentials)
115 | private static final String CLIENTID_PARAM = "clientId";
116 | private static final String CLIENT_SECRET_PARAM = "clientSecret";
117 |
118 | private static final String KEYSTORE_PARAM = "keyStore";
119 | private static final String KEYSTOREPASSWD_PARAM = "keyStorePasswd";
120 |
121 | public void setConnection(Application.Parameters cmdLineParams)
122 | {
123 | setConnection(cmdLineParams.getNamed().get(CLIENTID_PARAM),
124 | cmdLineParams.getNamed().get(CLIENT_SECRET_PARAM),
125 | cmdLineParams.getNamed().get(SERVICE_PARAM));
126 |
127 | String value = cmdLineParams.getNamed().get(KEYSTORE_PARAM);
128 | if (value != null) _keystoreFile = value.trim();
129 |
130 | value = cmdLineParams.getNamed().get(KEYSTOREPASSWD_PARAM);
131 | if (value != null) _keystorePasswd = value.trim();
132 | }
133 |
134 | public void setConnection(String clientId, String clientSecret, String service)
135 | {
136 | if (clientId != null) _clientId = clientId.trim();
137 | if (clientSecret != null) _clientSecret = clientSecret.trim();
138 | if (service != null) _service = service.trim();
139 | }
140 |
141 | public String _clientId = "";
142 | public String _clientSecret = "";
143 | public String _service = "";
144 | public String _keystoreFile = "keystore.jks"; // Within running directory
145 | public String _keystorePasswd = "Welcome1";
146 | }
147 |
148 | Stage m_dialog = new Stage();
149 |
150 | // Controls defined within the FXML configuration
151 | @FXML private TabPane layout;
152 |
153 | // ADS Properties
154 | @FXML private TextField f_host;
155 | @FXML private TextField f_service;
156 | @FXML private TextField f_user;
157 | @FXML private TextField f_appId;
158 | @FXML private TextField f_position;
159 | @FXML private Button f_ads_connectButton;
160 |
161 | // Real-Time -- Optimized V1 Properties
162 | @FXML private TextField f_machineId;
163 | @FXML private TextField f_password;
164 | @FXML private TextField f_appKey;
165 | @FXML private Button f_rto_v1_connectButton;
166 |
167 | // Real-Time -- Optimized V2 Properties
168 | @FXML private TextField f_clientId;
169 | @FXML private TextField f_clientSecret;
170 | @FXML private Button f_rto_v2_connectButton;
171 |
172 | // Connection/registration parameters
173 | ADSConnection m_adsConnection = new ADSConnection();
174 | RTO_V1Connection m_rto_v1_Connection = new RTO_V1Connection();
175 | RTO_V2Connection m_rto_v2_Connection = new RTO_V2Connection();
176 |
177 | // EMA consumer
178 | SpeedGuideConsumer m_consumer;
179 |
180 | public void initialize(Application.Parameters cmdLineParams, SpeedGuideConsumer consumer)
181 | {
182 | m_adsConnection.setConnection(cmdLineParams);
183 | m_rto_v1_Connection.setConnection(cmdLineParams);
184 | m_rto_v2_Connection.setConnection(cmdLineParams);
185 | m_consumer = consumer;
186 |
187 | m_consumer.setRegion(cmdLineParams.getNamed().get("region"));
188 |
189 | // Define the main viewing scene,
190 | Scene scene = new Scene(layout, layout.getPrefWidth(), layout.getPrefHeight());
191 |
192 | // Assign to our main stage and show the application to the end user
193 | m_dialog.setTitle("Connection Values");
194 | m_dialog.setScene(scene);
195 | m_dialog.initModality(Modality.APPLICATION_MODAL);
196 | m_dialog.initStyle(StageStyle.UTILITY);
197 | m_dialog.setResizable(false);
198 |
199 | // Bind properties
200 | f_ads_connectButton.disableProperty().bind(Bindings.isEmpty(f_host.textProperty()));
201 | f_rto_v1_connectButton.disableProperty().bind(Bindings.isEmpty(f_machineId.textProperty())
202 | .or(Bindings.isEmpty(f_password.textProperty())
203 | .or(Bindings.isEmpty(f_appKey.textProperty()))));
204 | f_rto_v2_connectButton.disableProperty().bind(Bindings.isEmpty(f_clientId.textProperty())
205 | .or(Bindings.isEmpty(f_clientSecret.textProperty())));
206 | }
207 |
208 | @FXML
209 | private void clickedADSConnect() {
210 | m_dialog.close();
211 | m_adsConnection.setConnection(f_host.getText(), f_service.getText(), f_user.getText(), f_appId.getText(), f_position.getText());
212 | m_consumer.defineADSConsumer(m_adsConnection);
213 | }
214 |
215 | @FXML
216 | private void clickedRTO_V1Connect() {
217 | m_dialog.close();
218 | m_rto_v1_Connection.setConnection(f_machineId.getText(), f_password.getText(), f_appKey.getText(), f_service.getText());
219 | m_consumer.defineRTO_V1Consumer(m_rto_v1_Connection);
220 | }
221 |
222 | @FXML
223 | private void clickedRTO_V2Connect() {
224 | m_dialog.close();
225 | m_rto_v2_Connection.setConnection(f_clientId.getText(), f_clientSecret.getText(), f_service.getText());
226 | m_consumer.defineRTO_V2Consumer(m_rto_v2_Connection);
227 | }
228 |
229 | @FXML
230 | private void onADSKeyReleased(KeyEvent event) {
231 | if (event.getCode() == KeyCode.ENTER && !f_ads_connectButton.isDisabled())
232 | clickedADSConnect();
233 | }
234 |
235 | @FXML
236 | private void onRTO_V1KeyReleased(KeyEvent event) {
237 | if (event.getCode() == KeyCode.ENTER && !f_rto_v1_connectButton.isDisabled())
238 | clickedRTO_V1Connect();
239 | }
240 |
241 | @FXML
242 | private void onRTO_V2KeyReleased(KeyEvent event) {
243 | if (event.getCode() == KeyCode.ENTER && !f_rto_v2_connectButton.isDisabled())
244 | clickedRTO_V2Connect();
245 | }
246 |
247 | // Connect
248 | // Make an attempt to connect to a streaming service. Based on the parameters chosen, we select the appropriate
249 | // server, i.e. ADS or RTO (RealTime Optimized).
250 | public void connect()
251 | {
252 | switch (getConnectionSelection()) {
253 | case 0:
254 | ads_connect();
255 | break;
256 | case 1:
257 | rto_v2_connect();
258 | break;
259 | case 2:
260 | rto_v1_connect();
261 | break;
262 | default:
263 | ads_connect();
264 | break;
265 | }
266 | }
267 |
268 | private int getConnectionSelection()
269 | {
270 | if ( !m_adsConnection._host.isEmpty())
271 | return 0;
272 | else if ( !m_rto_v2_Connection._clientId.isEmpty() || !m_rto_v2_Connection._clientSecret.isEmpty())
273 | return 1;
274 | else if ( !m_rto_v1_Connection._machineId.isEmpty() || !m_rto_v1_Connection._appKey.isEmpty() || !m_rto_v1_Connection._password.isEmpty() )
275 | return 2;
276 | else
277 | return 0;
278 | }
279 |
280 | private void ads_connect()
281 | {
282 | if ( m_adsConnection._host.isEmpty() )
283 | connectionDialog();
284 | else
285 | m_consumer.defineADSConsumer(m_adsConnection);
286 | }
287 |
288 | private void rto_v1_connect()
289 | {
290 | if ( m_rto_v1_Connection._machineId.isEmpty() || m_rto_v1_Connection._password.isEmpty() || m_rto_v1_Connection._appKey.isEmpty() )
291 | connectionDialog();
292 | else
293 | m_consumer.defineRTO_V1Consumer(m_rto_v1_Connection);
294 | }
295 |
296 | private void rto_v2_connect()
297 | {
298 | if ( m_rto_v2_Connection._clientId.isEmpty() || m_rto_v2_Connection._clientSecret.isEmpty() )
299 | connectionDialog();
300 | else
301 | m_consumer.defineRTO_V2Consumer(m_rto_v2_Connection);
302 | }
303 |
304 | // Display the connection Dialog - index:0 (ADS), index:1 (RTO V2 Authentication), index:2 (RTO V1 Authentication)
305 | public void connectionDialog()
306 | {
307 | int index = getConnectionSelection();
308 |
309 | // ADS Properties
310 | f_host.setText(m_adsConnection._host);
311 | f_service.setText(m_adsConnection._service);
312 | f_user.setText(m_adsConnection._user);
313 | f_appId.setText(m_adsConnection._appId);
314 | f_position.setText(m_adsConnection._position);
315 |
316 | // RTO V1 Properties
317 | f_machineId.setText(m_rto_v1_Connection._machineId);
318 | f_password.setText(m_rto_v1_Connection._password);
319 | f_appKey.setText(m_rto_v1_Connection._appKey);
320 | f_service.setText(m_rto_v1_Connection._service);
321 |
322 | // RTO V2 Properties
323 | f_clientId.setText(m_rto_v2_Connection._clientId);
324 | f_clientSecret.setText(m_rto_v2_Connection._clientSecret);
325 | f_service.setText(m_rto_v2_Connection._service);
326 |
327 | SingleSelectionModel selectionModel = layout.getSelectionModel();
328 | selectionModel.clearAndSelect(index);
329 | m_dialog.showAndWait();
330 | }
331 | }
332 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Speed Guide
2 | The Speed Guide utility allows users and developers, who do not have access to **LSEG Workspace/Eikon** desktop applications, a simple and quick way to easily browse market data content available within Refinitiv's Data Platform. The following guide outlines the fundamental purpose of speed guides and provides basic instructions to use the utility. In addition, outlines the components and basic instructions to build the tool using the source code available within this project.
3 |
4 |
5 | # Overview
6 |
7 | The Speed Guide utility allows users and developers who do not have access to the desktop application to browse market data content available from Refinitiv's Data Platform. The utility provides access to either cloud-based, Real-Time -- Optimized services or directly to your deployed Real-Time servers.
8 |
9 | When building applications consuming streaming market data, developers often need a list of RICs (Refinitiv Instrument Codes), and the values they contain, for certain market, exchange, or instrument types. The list of fields provided for these instrument types will differ depending on the type of asset. To aid in the discovery and understanding of these assets, the Speed Guide utility is a graphical tool presenting data screen displays. These data screens, or ***speed guides***, help users navigate through the universe of RICs and list the fields available for the specific asset. Developers will be presented with a simple organization of the data to gain a better understanding which includes complex structures such as Option Chains, Indices, Futures, etc.
10 |
11 | The Speed Guide tool registers for Snapshot only data content (i.e, non-streaming).
12 |
13 | ## Utility download
14 |
15 | The executable program and Readme is available for Download within the [LSEG Developer Platform](https://developers.lseg.com/en/tools-catalog/speedguide).
16 |
17 | ## Running the Utility
18 |
19 | The Speed Guide utility provides the ability to connect directly to the Refinitiv Data Platform, via Refinitiv Real-Time -- Optimized or access through your deployed Real-Time streaming server (ADS) available within the Refinitiv RTDS (Real-Time Distribution System).
20 |
21 | The package includes 2 components offering multiple ways to launch the tool. Packaged are:
22 |
23 | * **SpeedGuide.jar**: An executable JAR available for Windows, Mac or Linux
24 | * **SpeedGuide.exe**: A convenient windows wrapper
25 |
26 | **Note**: Because the package was built using JDK 1.8. which conveniently packages the JavaFx package, user must ensure they install Java 8 runtime.
27 | ### Launching the tool from the desktop (Windows):
28 |
29 | Double-clicking either the _.jar_ or _.exe_ file will not pass any required connection parameters to the application. However, users can create a shortcut on their desktop and apply parameters there - see [Command-line Options](#Command-line-options) below.
30 |
31 | If the required parameters are not specified, the application will present a [Connection Dialog](#usage) requesting for the required connection details. In either case, no console is involved thus no additional messages, such as log messages, can be viewed.
32 |
33 | **Note**: Launching the executable JAR requires the [Javaw](https://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html) program to open it. When not associated, you will be presented with a request such as:
34 |
35 | 
36 |
37 | You will need to choose the Javaw program within your Java installation.
38 |
39 | ### Launching the tool from the console
40 |
41 | At the console, you can pass command-line parameters to the utility:
42 |
43 | * #### Launching the executable JAR
44 |
45 | \> **java -jar SpeedGuide.jar [connection parameters]**
46 |
47 | When launching the executable JAR, users optionally specify command-line options and have the opportunity to see the output on the console.
48 |
49 | * #### Launching the windows wrapper EXE
50 |
51 | \> **SpeedGuide.exe [options]**
52 |
53 | The windows wrapper is strictly a GUI based facility that does not have an explicit console attached. Thus, no output can be viewed on the console. However, users can capture the output within a file. for example:
54 |
55 | \> **SpeedGuide.exe>output.txt [connection parameters]**
56 |
57 |
58 | #### Command-line Options
59 |
60 | --service=serviceName Optional. Service Name providing market data content.
61 | Eg: ELEKTRON_DD. Default: Determined from Directory response.
62 |
63 | ************* ADS Connection Parameters **************
64 | --host=hostname:port Required. ADS Server address/hostname and port of your Market Data
65 | server. Syntax: :. Eg: elektron:14002 or 192.168.1.1:14002
66 | --user=userName Optional. DACS User name required if authentication is enabled on server.
67 | Note: if no user name is provided, the utility will use your desktop login
68 | --appid=ApplicationId Optional. DACS Application ID if authentication is enabled on server.
69 | Application ID has no default.
70 | --position=Position Optional. DACS Position if authentication is enabled on server.
71 | Position has no default.
72 |
73 | ************* Real-Time -- Optimized Connection Parameters **************
74 | --region=location Optional. Specify the location to connect within the cloud.
75 | Eg: ap-northeast-1 (Asia) eu-west-1 (EU) us-east-2 (US). Default: us-east-1
76 |
77 | **** Version 1 Authentication >
78 | --machineId=machine ID Required. Real-Time -- Optimized Machine ID/User required for OAuth Password Grant.
79 | Eg: GE-A-00000000-1-8888
80 | --password=password Required. Real-Time -- OPtimized password required for OAuth Password Grant.
81 | Eg: Sunshine_1_UserPass
82 | --appKey=App Key Required. Real-Time -- Optimized AppKey or Client ID required for server authentication.
83 | Eg: x888x8x88888888x88888x88x8888xx88x88888x
84 |
85 | **** Version 2 Authentication >
86 | --clientId=Client ID Required. Real-Time -- Optimized Client/Service Account ID required for OAuth Client Credentials.
87 | Eg: GE-XXXXXXXXXXXX
88 | --clientSecret=secret Required. Real-Time -- Optimized Client secret required for OAuth Client Credentials.
89 | Eg: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
90 |
91 | --keyStore=keystorefile Optional. A Java KeyStore (JKS) required for secure package exchange.
92 | Default: SpeedGuide provides a file for convenience.
93 | --keyStorePasswd=passwd Optional. Password associated with the specified keystore file.
94 | Default: SpeedGuide includes the password for the default keystore file.
95 |
96 |
97 | --d[ebug] Debug Mode. Display verbose messages to the console
98 | --h[elp] Prints this screen
99 | The following example shows the command-line parameters to connect to either an ADS or directly to Real-Time -- Optimized in the cloud.
100 |
101 | * SpeedGuide.exe --host=myserver:14002 --service=ELEKTRON_AD --user=testuser --appid=256 --position=127.0.0.1
102 | * SpeedGuide.exe --clientId=GE-XXXX1234XXXX --clientSecret=9x999999-9xxx-9999-9x99-9x9xx99x9x99
103 | * SpeedGuide.exe --clientId=GE-123X9ABCDE9Z --clientSecret=9z123456-9abc-5555-9a12-1a2bc34d5e67 --region=eu-west-1
104 |
105 | ## Usage
106 |
107 | When launching the utility specifying insufficient command-line options, the user will be presented with a Connection dialog. The user can choose how they want to access the platform, i.e. through their deployed real-time services, via ADS:
108 |
109 | 
110 |
111 | Or directly to the cloud via Real-Time -- Optimized (Version 1 authentication):
112 |
113 | 
114 |
115 | Or directly to the cloud via Real-Time -- Optimized (Version 2 authentication):
116 |
117 | 
118 |
119 | The _Status Pane_ at the bottom of the main window provides some general feedback, whether success or failure. In the case where a successful connection can be made to your specified server, the utility will launch the root Speed Guide item, i.e. **REFINITIV**.
120 |
121 | **Note**: While the utility will launch the "REFINITIV" RIC, which represents the root code of the Speed Guide pages, the utility is extremely useful for developers to visualize any other instrument available as well as optionally selecting an appropriate _Region_ when connecting to the cloud.
122 |
123 | 
124 |
125 | ### Buttons
126 |
127 | The _Home_ button takes the user back to this page at any time.
128 |
129 | To navigate through the guide, double click on any text nested between **< >** characters. Although suggested within the guides, this utility does not presently support NEWS codes nested between **[ ]** characters.
130 |
131 | The top navigation menu provides:
132 |
133 | * A _Home_ button to go back to the initial main page.
134 | * A _Previous_ button to go back to the previous page (if it exists).
135 | * A _Next_ button to go to next page (if it exists).
136 | * An input text field that displays the RIC for the current page, and allows entering a RIC to request.
137 | * A _Connect_ button when no connection has been established
138 | * For cloud-based connections, the _RTO Region_ provides the opportunity to switch your region
139 | * A _Service_ specification drop-down providing the list of services available within the connected server
140 |
141 | During normal operation, or possibly at startup, the _Status Pane_ will highlight issues appropriately. In the case where the user specifies an incorrect service, the _Service_ drop-down will give them the opportunity to select a valid one.
142 |
143 | 
144 |
145 | ### Data Navigation
146 |
147 | As you navigate through the guide, double-clicking on \<**_nested items_**\>, you may be presented with a simple fieldlist display of the IDs, or Field IDs (FIDs) for a given RIC.
148 |
149 | For example:
150 |
151 | 
152 |
153 | Although not presented in a user-friendly display form, the native list of fields not only provides the user the ability to research all available fields for a given asset, but also the opportunity to better understand the structure of LSEG Data.
154 |
155 | #### Chains
156 |
157 | Navigating through the guide, you will likely come across some more complex data structures. For example, some market data elements, such as the Nasdaq Top 25, are represented as a collection of elements referred to as a **_Chain_**. A _Chain_ contains a dynamically-sized collection of elements represented within a static list of underlying fields within a market data record. In the case of the Nasdaq Top 25, there will be a total of 25 links. Because a Chain is represented within a simple _MarketPrice_ structure containing a static number of links, the structure offers the ability to pull up the next link within this collection to allow applications to navigate through the chain to retrieve the desired collection.
158 |
159 | To demonstrate, we manually entered the Nasdaq Top 25 index _.AV.O_:
160 |
161 | 
162 |
163 | We can see from above, the record contains a preset number of elements (1-14) and the ability to pull up the next group of elements within the _next link_. For a detailed outline of Chains, refer to the article: [Simple Chain Objects](https://developers.lseg.com/article/simple-chain-objects-ema-part-1) within the Developer Community.
164 |
165 | Feel free to navigate through the guide to discover many other assets and data elements offered by Refinitiv.
166 |
167 | # Solution Code
168 |
169 | The utility was developed using the [Refinitiv Real-Time SDK - Java](https://developers.lseg.com/en/api-catalog/refinitiv-real-time-opnsrc/rt-sdk-java) and Java's GUI library Java 8 - JavaFx.
170 |
171 | ## Prerequisites
172 |
173 | Required software components:
174 |
175 | * [Refinitiv Real-Time SDK - Java](https://developers.lseg.com/en/api-catalog/refinitiv-real-time-opnsrc/rt-sdk-java) (2.0.1.L1 or greater) - Refinitiv interface to streaming, real-time services.
176 | * [JDK 8](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) - Java Development Kit - version 8.
177 | * [VScode](https://code.visualstudio.com/Download) - Visual Studio Code
178 |
179 |
180 | ## Building and running
181 |
182 | The Java FXML project is a Maven-based solution that generates the desired package for distribution. As defined within the pom.xml configuration file, the packaging uses
183 | the Launch4j plugin to help prepare a Windows package. Users can optionally remove this stanza if there is no desire to generate an EXE file for Windows. If
184 | there is a desire to use this plugin, it assumes the following JDK installation (Note: you can update to suit your environment)
185 |
186 | ```
187 |
188 | C:\Program Files\Common Files\Oracle\javapath
189 | 1.8.0
190 |
191 | ```
192 |
193 | The project was built using VS Code and Maven.
194 |
195 | ## Contributing
196 |
197 | Please read [CONTRIBUTING.md](https://gist.github.com/PurpleBooth/b24679402957c63ec426) for details on our code of conduct, and the process for submitting pull requests to us.
198 |
199 | ## Authors
200 |
201 | | **Name** | **Release** | **Details** |
202 | | --- | --- | --- |
203 | | Nick Zincone | Release 4.1.0 | Ability to specify region |
204 | | | | Default service based on Directory interrogation |
205 | | Nick Zincone | Release 4.0.0 | Added access to Real-Time -- Optimized v2 authentication. Rebranded (LSEG) |
206 | | Nick Zincone | Release 3.2 | Fixed dictionary download issue; rebuilt using newly branded (Refinitiv) SDK |
207 | | Nick Zincone | Release 3.1 | Fixed connectivity issues and re branded. |
208 | | Nick Zincone | Release 3.0 | Added access to Real-Time -- Optimized, formally ERT in Cloud, streaming services. |
209 | | Nick Zincone | Release 2.1 | Added DACS fields required for login to Elektron. |
210 | | Nick Zincone | Release 2.0 | Additional error checking |
211 | | | | Utilized JavaFX Scene Builder to generate FXML |
212 | | Susana Chang | Release 1.1 | Initial implementation |
213 |
214 |
215 |
216 |
217 |
218 |
219 | ## License
220 |
221 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details
222 |
--------------------------------------------------------------------------------
/src/main/java/com/lseg/ema/example/gui/ConsumerClient.java:
--------------------------------------------------------------------------------
1 | package com.lseg.ema.example.gui;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import com.lseg.ema.example.gui.SpeedGuide.StatusIndicator;
7 | import com.lseg.ema.example.gui.view.SpeedGuideViewController;
8 | import com.refinitiv.ema.access.AckMsg;
9 | import com.refinitiv.ema.access.Data;
10 | import com.refinitiv.ema.access.DataType;
11 | import com.refinitiv.ema.access.DataType.DataTypes;
12 | import com.refinitiv.ema.access.EmaFactory;
13 | import com.refinitiv.ema.access.FieldEntry;
14 | import com.refinitiv.ema.access.FieldList;
15 | import com.refinitiv.ema.access.GenericMsg;
16 | import com.refinitiv.ema.access.Msg;
17 | import com.refinitiv.ema.access.OmmConsumerClient;
18 | import com.refinitiv.ema.access.OmmConsumerEvent;
19 | import com.refinitiv.ema.access.RefreshMsg;
20 | import com.refinitiv.ema.access.RmtesBuffer;
21 | import com.refinitiv.ema.access.StatusMsg;
22 | import com.refinitiv.ema.access.UpdateMsg;
23 |
24 | public class ConsumerClient implements OmmConsumerClient {
25 | private RmtesBuffer _rmtesBuffer = EmaFactory.createRmtesBuffer();
26 | private String _compoundPage = "";
27 | private int _compoundPageTotalCount =0;
28 | private int _compoundPageCount =0;
29 | private boolean _isCompoundPage = false;
30 | private List _compoundPageRicList = new ArrayList();
31 | private String _compoundPageRIC ="";
32 | private String _previousRicInChain ="";
33 | private boolean m_debug = false;
34 | private SpeedGuideViewController m_viewController;
35 | private SpeedGuideConsumer m_consumer;
36 |
37 | public void setConsumer(SpeedGuideConsumer consumer) {
38 | m_consumer = consumer;
39 | }
40 |
41 | public void setViewController(SpeedGuideViewController viewController) {
42 | m_viewController = viewController;
43 | }
44 |
45 | public void setDebug(boolean debug) {
46 | m_debug = debug;
47 | }
48 |
49 | /**
50 | * onRefreshMsg
51 | * Handles REFRESH messages
52 | */
53 |
54 | public void onRefreshMsg(RefreshMsg refreshMsg, OmmConsumerEvent event)
55 | {
56 | String item = (refreshMsg.hasName() ? refreshMsg.name() : "");
57 | String service = (refreshMsg.hasServiceName() ? refreshMsg.serviceName() : "");
58 |
59 | String status = "Item: " + item + " / Service: " + service;
60 |
61 | if (m_debug )
62 | {
63 | System.out.println("REFRESH");
64 | System.out.println("Item Name: " + item);
65 | System.out.println("Service Name: " + service);
66 | System.out.println("State: " + refreshMsg.state());
67 | }
68 |
69 | status += " [State: " + refreshMsg.state() + "]";
70 | m_viewController.updateStatus(status, StatusIndicator.RESPONSE_SUCCESS);
71 |
72 | if (DataType.DataTypes.FIELD_LIST == refreshMsg.payload().dataType())
73 | decodeRefresh(refreshMsg.payload().fieldList(), item);
74 |
75 | if (m_debug) System.out.println();
76 | }
77 |
78 | /**
79 | * onUpdateMsg
80 | * Handles the UPDATE Messages
81 | */
82 | public void onUpdateMsg(UpdateMsg updateMsg, OmmConsumerEvent event)
83 | {
84 | System.out.println("Updates not yet supported");
85 | }
86 |
87 | public void onStatusMsg(StatusMsg statusMsg, OmmConsumerEvent event)
88 | {
89 | String item = (statusMsg.hasName() ? statusMsg.name() : "");
90 | String service = (statusMsg.hasServiceName() ? statusMsg.serviceName() : "");
91 |
92 | String status = "Item: " + item + " / Service: " + service;
93 |
94 | if (m_debug)
95 | {
96 | System.out.println("STATUS MSG");
97 | System.out.println("Item Name: " + item);
98 | System.out.println("Service Name: " + service);
99 | }
100 |
101 | if (statusMsg.hasState()) {
102 | status += " [State: " +statusMsg.state() + "]";
103 | if (m_debug) System.out.println("State: " +statusMsg.state());
104 | }
105 |
106 | if (m_debug) System.out.println();
107 | m_viewController.updateStatus(status, StatusIndicator.RESPONSE_ERROR, item);
108 | }
109 |
110 | public void onGenericMsg(GenericMsg genericMsg, OmmConsumerEvent consumerEvent){}
111 | public void onAckMsg(AckMsg ackMsg, OmmConsumerEvent consumerEvent){}
112 | public void onAllMsg(Msg msg, OmmConsumerEvent consumerEvent){}
113 |
114 |
115 | /**
116 | * Decode REFRESH Messages
117 | *
118 | * @param fieldList
119 | * @param ric
120 | */
121 | void decodeRefresh(FieldList fieldList, String ric)
122 | {
123 | String pageStr = "";
124 | String quoteStr = "";
125 | boolean _isPage = false;
126 | int width = 21;
127 | String fieldDisplay;
128 |
129 | long pref_disp = 0, recordtype = 0, rdndisplay = 0;
130 | List compoundPageRicList = new ArrayList();
131 |
132 | for (FieldEntry fieldEntry : fieldList)
133 | {
134 | fieldDisplay = String.format("%-"+width+"s", fieldEntry.name() + " (" + fieldEntry.fieldId() + "):");
135 |
136 | if (m_debug) System.out.print("Fid: " + fieldEntry.fieldId() + " Name = " + fieldEntry.name() + " DataType: " +
137 | DataType.asString(fieldEntry.load().dataType()) + " Value: ");
138 |
139 | if (Data.DataCode.BLANK == fieldEntry.code())
140 | {
141 | if (m_debug) System.out.println(" blank");
142 | }
143 | else
144 | {
145 | switch (fieldEntry.fieldId())
146 | {
147 | case 215 : // Page record rows (14X64)
148 | case 216 :
149 | case 217 :
150 | case 218 :
151 | case 219 :
152 | case 220 :
153 | case 221 :
154 | case 222 :
155 | case 223 :
156 | case 224 :
157 | case 225 :
158 | case 226 :
159 | case 227 :
160 | case 228 :
161 | case 315 : // Page record rows (25x80)
162 | case 316 :
163 | case 317 :
164 | case 318 :
165 | case 319 :
166 | case 320 :
167 | case 321 :
168 | case 322 :
169 | case 323 :
170 | case 324 :
171 | case 325 :
172 | case 326 :
173 | case 327 :
174 | case 328 :
175 | case 329 :
176 | case 330 :
177 | case 331 :
178 | case 332 :
179 | case 333 :
180 | case 334 :
181 | case 335 :
182 | case 336 :
183 | case 337 :
184 | case 338 :
185 | case 339 :
186 | _isPage = true;
187 | if (fieldEntry.loadType() == DataTypes.RMTES) {
188 | String s = (_rmtesBuffer.apply(fieldEntry.rmtes())).toString();
189 | pageStr += s + SpeedGuide.NEWLINE;
190 | if (m_debug) System.out.println(s);
191 | }
192 | break;
193 | case 240 : // These are the RIC list of the Chain
194 | case 241 :
195 | case 242 :
196 | case 243 :
197 | case 244 :
198 | case 245 :
199 | case 246 :
200 | case 247 :
201 | case 248 :
202 | case 249 :
203 | case 250 :
204 | case 251 :
205 | case 252 :
206 | case 253 :
207 |
208 | case 800 : // These are the RIC list of the Chain
209 | case 801 :
210 | case 802 :
211 | case 803 :
212 | case 804 :
213 | case 805 :
214 | case 806 :
215 | case 807 :
216 | case 808 :
217 | case 809 :
218 | case 810 :
219 | case 811 :
220 | case 812 :
221 | case 813 :
222 | case 814 :
223 |
224 | if (fieldEntry.loadType() == DataTypes.ASCII) {
225 | String chainRic = fieldEntry.ascii().toString();
226 | quoteStr += fieldDisplay + "<" + chainRic + ">" + SpeedGuide.NEWLINE;
227 | if (m_debug) System.out.println(chainRic);
228 | compoundPageRicList.add(chainRic);
229 | }
230 | break;
231 | case 815 :
232 | case 1081 :
233 | case 238 :
234 | if (fieldEntry.loadType() == DataTypes.ASCII) {
235 | String s = fieldEntry.ascii().toString();
236 | quoteStr += fieldDisplay + "<" + s + ">" + SpeedGuide.NEWLINE;
237 | if (m_debug) System.out.println(s);
238 | }
239 | break;
240 | case 6:
241 | case 11:
242 | case 12:
243 | case 13:
244 | case 21:
245 | case 22:
246 | case 25:
247 | case 56:
248 | if (fieldEntry.loadType() == DataTypes.REAL) {
249 | double last = fieldEntry.real().asDouble();
250 | if (m_debug) System.out.println(last);
251 | quoteStr += fieldDisplay + last + SpeedGuide.NEWLINE;
252 | }
253 | break;
254 | case 239 :// Fid: 239 Name = REF_COUNT (CHAIN) DataType: UInt Value: 2
255 | if (fieldEntry.loadType() == DataTypes.UINT) {
256 | long ref_count = fieldEntry.uintValue();
257 | if (m_debug) System.out.println("REF_COUNT =" + fieldEntry.uintValue());
258 | quoteStr += fieldDisplay + ref_count + SpeedGuide.NEWLINE;
259 | }
260 | break;
261 | case 1080 :// Fid: 1080 Name = PREF_DISP DataType: UInt Value: 1291
262 | if (fieldEntry.loadType() == DataTypes.UINT) {
263 | pref_disp = fieldEntry.uintValue();
264 | if (m_debug) System.out.println("PREF_DISP =" + fieldEntry.uintValue());
265 | quoteStr += fieldDisplay + pref_disp + SpeedGuide.NEWLINE;
266 | }
267 | break;
268 | case 259 :// Fid: 259 Name = RECORDTYPE DataType: UInt Value: RECORDTYPE =234
269 | if (fieldEntry.loadType() == DataTypes.UINT) {
270 | recordtype = fieldEntry.uintValue();
271 | if (m_debug) System.out.println("RECORDTYPE =" + fieldEntry.uintValue());
272 | quoteStr += fieldDisplay + recordtype + SpeedGuide.NEWLINE;
273 | }
274 | break;
275 | case 2 :// RDNDISPLAY DataType: UInt Value: 230
276 | if (fieldEntry.loadType() == DataTypes.UINT) {
277 | rdndisplay = fieldEntry.uintValue();
278 | if (m_debug) System.out.println("RDNDISPLAY =" + fieldEntry.uintValue());
279 | quoteStr += fieldDisplay + rdndisplay + SpeedGuide.NEWLINE;
280 | }
281 | break;
282 |
283 | case 3 :// DSPLY_NAME
284 | if (fieldEntry.loadType() == DataTypes.RMTES) {
285 | String dsply_name= (_rmtesBuffer.apply(fieldEntry.rmtes())).toString();
286 | pageStr += fieldEntry.name() + ": "+ dsply_name+SpeedGuide.NEWLINE;
287 | if (m_debug) System.out.println( "dsply_name = "+ dsply_name);
288 | quoteStr += fieldDisplay + dsply_name + SpeedGuide.NEWLINE;
289 | }
290 | break;
291 | default :
292 |
293 | switch (fieldEntry.loadType())
294 | {
295 | case DataTypes.RMTES :
296 | String s = (_rmtesBuffer.apply(fieldEntry.rmtes())).toString();
297 | if (m_debug)
298 | {
299 | System.out.print( " DataType: " + DataType.asString(fieldEntry.load().dataType()) + " Value: ");
300 | System.out.println(s);
301 | }
302 | quoteStr += fieldDisplay + s + SpeedGuide.NEWLINE;
303 | break;
304 | case DataTypes.REAL :
305 | if (m_debug) System.out.println(fieldEntry.real().asDouble());
306 | quoteStr += fieldDisplay + fieldEntry.real().asDouble() + SpeedGuide.NEWLINE;
307 | break;
308 | case DataTypes.DATE :
309 | if (m_debug) System.out.println(fieldEntry.date().day() + " / " + fieldEntry.date().month() + " / " + fieldEntry.date().year());
310 | quoteStr += fieldDisplay + fieldEntry.date().day() + " / " + fieldEntry.date().month() + " / " + fieldEntry.date().year() + SpeedGuide.NEWLINE;
311 | break;
312 | case DataTypes.TIME :
313 | if (m_debug) System.out.println(fieldEntry.time().hour() + ":" + fieldEntry.time().minute() + ":" + fieldEntry.time().second() + ":" + fieldEntry.time().millisecond());
314 | quoteStr += fieldDisplay + fieldEntry.time().hour() + ":" + fieldEntry.time().minute() + ":" +
315 | fieldEntry.time().second() + ":" + fieldEntry.time().millisecond() + SpeedGuide.NEWLINE;
316 | break;
317 | case DataTypes.INT :
318 | if (m_debug) System.out.println(fieldEntry.intValue());
319 | quoteStr += fieldDisplay + fieldEntry.intValue() + SpeedGuide.NEWLINE;
320 | break;
321 | case DataTypes.UINT :
322 | quoteStr += fieldDisplay + fieldEntry.uintValue() + SpeedGuide.NEWLINE;
323 | if (m_debug) System.out.println(fieldEntry.uintValue());
324 | break;
325 | case DataTypes.ASCII :
326 | if (m_debug) System.out.println(fieldEntry.ascii());
327 | quoteStr += fieldDisplay + fieldEntry.ascii() + SpeedGuide.NEWLINE;
328 | break;
329 | case DataTypes.ENUM :
330 | if (m_debug) System.out.println(fieldEntry.enumValue());
331 | quoteStr += fieldDisplay + fieldEntry.enumValue() + SpeedGuide.NEWLINE;
332 | break;
333 | case DataTypes.ERROR :
334 | if (m_debug) System.out.println("(" + fieldEntry.error().errorCodeAsString() + ")");
335 | break;
336 | default :
337 | if (m_debug) System.out.println();
338 | break;
339 | }
340 |
341 | break;
342 | }
343 | }
344 |
345 | } // END - for loop - FieldEntry
346 |
347 | // Check if compound Page, if so subscribe to all RICs in chain and concatenate Pages
348 | //if ( pref_disp == 1291 && recordtype == 234 && rdndisplay == 230 ) {
349 | if ( pref_disp == 1291 || pref_disp == 3221)
350 | {
351 | _isCompoundPage = true;
352 | _compoundPageCount = 0;
353 | _compoundPage ="";
354 | _compoundPageTotalCount = compoundPageRicList.size();
355 | _compoundPageRIC = ric;
356 |
357 | _compoundPageRicList = new ArrayList(compoundPageRicList);
358 | if ( _compoundPageCount < _compoundPageTotalCount )
359 | {
360 | String compRic = compoundPageRicList.get(_compoundPageCount).trim();
361 | _previousRicInChain = compRic;
362 | if (m_debug) System.out.println("Subscribing to compound page Ric="+compRic);
363 | m_consumer.subscribe(compRic);
364 | }
365 | pageStr ="";
366 | }
367 | if ( _compoundPageCount < _compoundPageTotalCount ) {
368 | if (_previousRicInChain.equals(ric)) {
369 | String compRic = _compoundPageRicList.get(_compoundPageCount).trim();
370 | _previousRicInChain = compRic;
371 | if (m_debug) System.out.println("Subscribing to compound page Ric="+compRic);
372 | m_consumer.subscribe(compRic);
373 | }
374 | }
375 |
376 | /**
377 | * Fid: 2 Name = RDNDISPLAY DataType: UInt Value: 151
378 | * Fid: 259 Name = RECORDTYPE DataType: UInt Value: RECORDTYPE =234
379 | * Fid: 5357 Name = CONTEXT_ID DataType: Real Value: 2704.0
380 | * Fid: 6401 Name = DDS_DSO_ID DataType: UInt Value: 4118
381 | */
382 |
383 | /**
384 | * Fid: 2 Name = RDNDISPLAY DataType: UInt Value: RDNDISPLAY =151
385 | * Fid: 259 Name = RECORDTYPE DataType: UInt Value: RECORDTYPE =226
386 | */
387 |
388 | if ( _isCompoundPage )
389 | {
390 | _compoundPage += pageStr;
391 | _compoundPageCount ++;
392 | if (m_debug) System.out.println("_compoundPageTotalCount =" + _compoundPageTotalCount + " _compoundPageCount=" +_compoundPageCount);
393 |
394 | if (_compoundPageCount == (_compoundPageTotalCount+1) ) {
395 | m_viewController.updateTxtArea( _compoundPageRIC, _compoundPage );
396 | _compoundPageTotalCount = 0;
397 | _compoundPageCount = 0;
398 | _isCompoundPage = false;
399 | _compoundPageRicList.clear();
400 | }
401 | } else {
402 |
403 | //if ((recordtype == 226) || (recordtype == 234) || (recordtype == 249) || (recordtype == 201) || (recordtype == 217) || (recordtype == 25) || (recordtype == 41) || (recordtype == 89) )
404 | if ( _isPage )
405 | m_viewController.updateTxtArea( ric, pageStr );
406 | else
407 | m_viewController.updateTxtArea( ric, quoteStr );
408 | }
409 | }
410 | }
411 |
--------------------------------------------------------------------------------
/src/main/java/com/lseg/ema/example/gui/view/SpeedGuideViewController.java:
--------------------------------------------------------------------------------
1 | package com.lseg.ema.example.gui.view;
2 |
3 | import javafx.application.Platform;
4 | import javafx.beans.binding.Bindings;
5 | import javafx.beans.property.BooleanProperty;
6 | import javafx.beans.property.IntegerProperty;
7 | import javafx.beans.property.SimpleBooleanProperty;
8 | import javafx.beans.property.SimpleIntegerProperty;
9 | import javafx.beans.value.ChangeListener;
10 | import javafx.beans.value.ObservableValue;
11 | import javafx.collections.FXCollections;
12 | import javafx.collections.ObservableList;
13 | import javafx.fxml.FXML;
14 | import javafx.fxml.Initializable;
15 | import javafx.scene.Node;
16 | import javafx.scene.control.Button;
17 | import javafx.scene.control.ComboBox;
18 | import javafx.scene.control.IndexRange;
19 | import javafx.scene.control.Label;
20 | import javafx.scene.control.ScrollPane;
21 | import javafx.scene.control.TextArea;
22 | import javafx.scene.control.TextField;
23 | import javafx.scene.input.MouseEvent;
24 | import javafx.scene.layout.AnchorPane;
25 | import javafx.scene.layout.HBox;
26 | import javafx.scene.paint.Color;
27 | import javafx.scene.text.Text;
28 | import javafx.scene.text.TextFlow;
29 |
30 | import java.net.URL;
31 | import java.text.SimpleDateFormat;
32 | import java.util.ArrayList;
33 | import java.util.List;
34 | import java.util.ResourceBundle;
35 |
36 | import com.lseg.ema.example.gui.SpeedGuide;
37 | import com.lseg.ema.example.gui.SpeedGuideConsumer;
38 | import com.lseg.ema.example.gui.SpeedGuide.StatusIndicator;
39 |
40 | public class SpeedGuideViewController implements Initializable
41 | {
42 | // Controls defined within the FXML configuration
43 | @FXML private AnchorPane layout;
44 | @FXML private Label version;
45 | @FXML private Button home;
46 | @FXML private Button previous;
47 | @FXML private Button next;
48 | @FXML private TextField ric;
49 | @FXML private Button submit;
50 | @FXML private HBox regionsHBox;
51 | @FXML private ComboBox regions;
52 | @FXML private HBox servicesHBox;
53 | @FXML private ComboBox services;
54 | @FXML private HBox connectHBox;
55 | @FXML private Button connect;
56 | @FXML private TextArea textArea;
57 | @FXML private ScrollPane statusPane;
58 | @FXML private TextFlow statusText;
59 |
60 | private List m_listOfPages = new ArrayList();
61 | private List m_listOfRICs = new ArrayList();
62 |
63 | // Index is 1-based (pages 1..n) where n=pageTotal
64 | private IntegerProperty m_pageIndex = new SimpleIntegerProperty(0);
65 | private IntegerProperty m_pageTotal = new SimpleIntegerProperty(0);
66 |
67 | // Connection status
68 | private BooleanProperty m_connecting = new SimpleBooleanProperty(false);
69 | private BooleanProperty m_connected = new SimpleBooleanProperty(false);
70 |
71 | // RTO-enabled
72 | private BooleanProperty m_rtoEnabled = new SimpleBooleanProperty(false);
73 |
74 | private boolean m_debug = false;
75 | private SpeedGuideConsumer m_consumer;
76 | private SpeedGuideConnection m_connection;
77 |
78 | private ObservableList m_regions = FXCollections.observableArrayList();
79 | private ObservableList m_services = FXCollections.observableArrayList();
80 |
81 | @Override // Method called by the FXMLLoader when initialization is done
82 | public void initialize(URL fxmlFileLocation, ResourceBundle resources) {
83 | version.setText(version.getText() + SpeedGuide.VER_CODE);
84 | }
85 |
86 | public AnchorPane getLayout() {
87 | return(layout);
88 | }
89 |
90 | public void defineControlBindings(SpeedGuideConsumer consumer) {
91 | m_consumer = consumer;
92 |
93 | home.disableProperty().bind(Bindings.equal(m_pageTotal, 0));
94 | previous.disableProperty().bind(Bindings.lessThanOrEqual(m_pageIndex, 1));
95 | next.disableProperty().bind(Bindings.equal(m_pageIndex, m_pageTotal));
96 | ric.disableProperty().bind(Bindings.when(m_connected).then(false).otherwise(true));
97 | submit.disableProperty().bind(Bindings.when(m_connected).then(false).otherwise(true));
98 |
99 | // The region selection has the following states.
100 | // Enabled: When we are disconnected and need to manually select a different region
101 | // Disabled: When we are in the process of trying to connect
102 | // Visible: When we have no endpoint information.
103 | // Invisible: When we are connected.
104 | regionsHBox.visibleProperty().bind(m_rtoEnabled);
105 | regions.disableProperty().bind(m_connecting);
106 |
107 | // Determine what to display within the "RTO Regions" combo box when the control becomes visible
108 | regionsHBox.visibleProperty().addListener(new ChangeListener() {
109 | @Override public void changed(ObservableValue extends Boolean> o, Boolean oldVal, Boolean newVal)
110 | {
111 | // This will avoid a InvalidState Exception because we're not within the JavaFX thread
112 | Platform.runLater(new Runnable()
113 | {
114 | @Override
115 | public void run()
116 | {
117 | if ( newVal )
118 | {
119 | regions.setItems(m_regions);
120 | if (m_regions.contains(m_consumer.getRegion()))
121 | regions.setValue(m_consumer.getRegion());
122 | else
123 | regions.setPromptText(m_consumer.getRegion());
124 | }
125 | else
126 | m_regions.clear();
127 | }
128 | });
129 | }
130 | });
131 |
132 | // The connection button has the following states.
133 | // Enabled: When we are disconnected and need to manually connect
134 | // Disabled: When we are in the process of trying to connect
135 | // Visible: When we are disconnected.
136 | // Invisible: When we are connected.
137 | connectHBox.visibleProperty().bind(Bindings.when(m_connected).then(false).otherwise(true));
138 | connect.disableProperty().bind(Bindings.when(m_connecting).then(true).otherwise(false));
139 |
140 | servicesHBox.visibleProperty().bind(m_connected);
141 |
142 | // Determine what to display within the "Available Services" combo box when the control becomes visible
143 | servicesHBox.visibleProperty().addListener(new ChangeListener() {
144 | @Override public void changed(ObservableValue extends Boolean> o, Boolean oldVal, Boolean newVal)
145 | {
146 | // This will avoid a InvalidState Exception because we're not within the JavaFX thread
147 | Platform.runLater(new Runnable()
148 | {
149 | @Override
150 | public void run()
151 | {
152 | if ( newVal )
153 | {
154 | services.setItems(m_services);
155 | if (m_services.contains(m_consumer.getService()))
156 | services.setValue(m_consumer.getService());
157 | else
158 | services.setPromptText(m_consumer.getService());
159 | }
160 | else
161 | m_services.clear();
162 | }
163 | });
164 | }
165 | });
166 | }
167 |
168 | public void setConnection(boolean connected, boolean connecting, String pageStr) {
169 | // This will avoid a InvalidState Exception to ensure we are within the JavaFX thread
170 | Platform.runLater(new Runnable()
171 | {
172 | @Override
173 | public void run() {
174 | updateTxtArea("", pageStr);
175 | m_connected.set(connected);
176 | m_connecting.set(connecting);
177 | }
178 | });
179 | }
180 |
181 | public void setRTOConnection(boolean rtoConnection) {
182 | m_rtoEnabled.set(rtoConnection);
183 | }
184 |
185 | public boolean isConnected() {
186 | return(m_connected.get());
187 | }
188 |
189 | public boolean isConnecting() {
190 | return(m_connecting.get());
191 | }
192 |
193 | public void setConnectionViewController(SpeedGuideConnection connectionViewController) {
194 | m_connection = connectionViewController;
195 | }
196 |
197 | public void setDebug(boolean debug) {
198 | m_debug = debug;
199 | }
200 |
201 | public void addRegion(String region) {
202 | if (!m_regions.contains(region))
203 | m_regions.add(region);
204 | }
205 |
206 | public void addService(String service) {
207 | m_services.add(service);
208 | }
209 |
210 | @FXML
211 | private void clickedHome() {
212 | String pageStr = m_listOfPages.get(0);
213 | String item = m_listOfRICs.get(0);
214 |
215 | textArea.clear();
216 | textArea.setText(pageStr);
217 | ric.setText(item);
218 |
219 | // Reset
220 | m_pageIndex.set(1);
221 | if (m_debug) System.out.println("pageIndex=" + m_pageIndex.get() +" pageTotal=" + m_pageTotal.get());
222 | }
223 |
224 | @FXML
225 | private void clickedPrevious() {
226 |
227 | int pageIndex = m_pageIndex.get();
228 |
229 | // Decrement
230 | pageIndex--;
231 | m_pageIndex.set(pageIndex);
232 |
233 | String pageStr = m_listOfPages.get(pageIndex-1);
234 | String item = m_listOfRICs.get(pageIndex-1);
235 | textArea.clear();
236 | textArea.setText(pageStr);
237 | ric.setText(item);
238 | if (m_debug) System.out.println("pageIndex=" + m_pageIndex.get() +" pageTotal=" + m_pageTotal.get());
239 | }
240 |
241 | @FXML
242 | private void clickedNext() {
243 |
244 | int pageIndex = m_pageIndex.get();
245 |
246 | String pageStr = m_listOfPages.get(pageIndex);
247 | String item = m_listOfRICs.get(pageIndex);
248 | m_pageIndex.set(pageIndex+1);
249 |
250 | textArea.clear();
251 | textArea.setText(pageStr);
252 | ric.setText(item);
253 | if (m_debug) System.out.println("pageIndex=" + m_pageIndex.get() +" pageTotal=" + m_pageTotal.get());
254 | }
255 |
256 | @FXML
257 | private void enteredRic() {
258 | m_consumer.subscribe(ric.getText().trim());
259 | }
260 |
261 | @FXML
262 | private void clickedSubmit() {
263 | m_consumer.subscribe(ric.getText().trim());
264 | }
265 |
266 | @FXML
267 | private void clickedConnect() {
268 | m_connection.connectionDialog();
269 | }
270 |
271 | @FXML
272 | private void selectedRegion() {
273 | String selectedItem = regions.getSelectionModel().getSelectedItem();
274 | if ( selectedItem != null) {
275 | if (!selectedItem.equals(m_consumer.getRegion())) {
276 | m_consumer.setRegion(selectedItem);
277 | m_consumer.defineNewRegion();
278 | }
279 | }
280 | }
281 |
282 | @FXML
283 | private void selectedService() {
284 | String selectedItem = services.getSelectionModel().getSelectedItem();
285 | if ( selectedItem != null) {
286 | m_consumer.setService(selectedItem);
287 | m_consumer.subscribe(ric.getText().trim());
288 | }
289 | }
290 |
291 | /**
292 | * Add Event Listener handler for the TextArea:
293 | * 1. detect double click on <> and obtain (RIC) text inside of <>
294 | * 2. subscribe to RIC
295 | */
296 | @FXML
297 | private void clickedTextArea(MouseEvent event) {
298 | if (event.getClickCount() == 2) {
299 | // Get the range of the selected text
300 | IndexRange range = textArea.getSelection();
301 |
302 | if ( findRegion(range, '<', '>') ) { // Check if region is embedded within < > brackets
303 | String ric = textArea.getSelectedText().trim();
304 | if (m_debug) System.out.println("Selected text: [" + ric + "]");
305 | m_consumer.subscribe(ric);
306 | } else {
307 | if ( findRegion(range, '[', ']') ) { // Check if region is embedded within [ ] brackets
308 | String news = textArea.getSelectedText().trim();
309 | if (m_debug) System.out.println("Selected text: [" + news + "]");
310 | updateStatus("Only text within < > supported.", StatusIndicator.RESPONSE_ERROR);
311 | }
312 | }
313 |
314 | // Unmark region
315 | textArea.selectRange(0,0);
316 | }
317 | }
318 |
319 | // Mark off region embedded within the open and close tokens.
320 | private boolean findRegion(IndexRange startingRange, int openToken, int closeToken)
321 | {
322 | if ( findStartPoint(startingRange, openToken, closeToken) ) {
323 | int start = textArea.getSelection().getStart();
324 | if ( findEndPoint(startingRange, openToken, closeToken) ) {
325 | textArea.selectRange(start, textArea.getSelection().getEnd());
326 | return(true);
327 | }
328 | }
329 |
330 | // Selected text not embedded with open/close tokens
331 | return(false);
332 | }
333 |
334 | // Find the starting token location, if available, based on where the user double clicked
335 | private boolean findStartPoint(IndexRange range, int openToken, int closeToken)
336 | {
337 | int start = range.getStart();
338 | int end = range.getEnd();
339 |
340 | while (true) {
341 | textArea.selectRange(start-10, end);
342 | IndexRange selection = textArea.getSelection();
343 |
344 | // Determine if we've reached the start of the text area
345 | if ( selection.getStart() == start ) return(false);
346 |
347 | // Extract the text region of interest
348 | String region = textArea.getSelectedText();
349 |
350 | // Determine if we've encountered a close token
351 | int cpos = region.lastIndexOf(closeToken);
352 |
353 | // Determine if we've encountered an Open token
354 | int opos = region.lastIndexOf(openToken);
355 |
356 | if (cpos > opos || opos >= 0) {
357 | textArea.selectRange(selection.getStart()+opos+1, end);
358 | return(true);
359 | }
360 |
361 | // We should not be on the previous line
362 | if ( region.indexOf('\n') >= 0 ) return(false);
363 |
364 | start = selection.getStart();
365 | }
366 | }
367 |
368 | // Find the ending token location, if available, based on where the user double clicked
369 | private boolean findEndPoint(IndexRange range, int openToken, int closeToken)
370 | {
371 | int start = range.getStart();
372 | int end = range.getEnd();
373 |
374 | while (true) {
375 | textArea.selectRange(start, end+10);
376 | IndexRange selection = textArea.getSelection();
377 |
378 | // Determine if we've reached the end of the text area
379 | if ( selection.getEnd() == end ) return(false);
380 |
381 | // Extract the text region of interest
382 | String region = textArea.getSelectedText();
383 |
384 | // Determine if we've encountered a close token
385 | int cpos = region.indexOf(closeToken);
386 |
387 | // Determine if we've encountered an Open token
388 | int opos = region.indexOf(openToken);
389 |
390 | if (opos >= 0) {
391 | if ( cpos < 0 || opos < cpos ) return(false);
392 | }
393 |
394 | if (cpos >= 0) {
395 | textArea.selectRange(range.getStart(), start+cpos);
396 | return(true);
397 | }
398 |
399 | // We should not be on the previous line
400 | if ( region.indexOf('\n') >= 0 ) return(false);
401 |
402 | end = selection.getEnd();
403 | }
404 | }
405 |
406 | public void updateTxtArea( String item, String pageStr )
407 | {
408 | // This will avoid a InvalidState Exception to ensure we are within the JavaFX thread
409 | Platform.runLater(new Runnable()
410 | {
411 | @Override
412 | public void run()
413 | {
414 | ric.setText(item);
415 | textArea.setText(pageStr);
416 |
417 | if ( !item.isEmpty()) {
418 | // before adding the new page, check
419 | int pageTotal = m_pageTotal.get();
420 |
421 | m_listOfPages.add(pageStr);
422 | m_listOfRICs.add(item);
423 |
424 | pageTotal++;
425 | m_pageTotal.set(pageTotal);
426 | m_pageIndex.set(pageTotal);
427 | if (m_debug) System.out.println("pageIndex=" + m_pageIndex.get() +" pageTotal=" + pageTotal);
428 | }
429 | }
430 | });
431 | }
432 |
433 |
434 | /**
435 | * Update status
436 | *
437 | * @param status
438 | * @param indicator
439 | */
440 | public void updateStatus(String status, StatusIndicator indicator)
441 | {
442 | updateStatus(status, indicator, "");
443 | }
444 |
445 | public void updateStatus(String status, StatusIndicator indicator, String item)
446 | {
447 | // This will avoid a InvalidState Exception to ensure we are within the JavaFX thread
448 | Platform.runLater(new Runnable()
449 | {
450 | @Override
451 | public void run()
452 | {
453 | if ( !item.isEmpty() )
454 | ric.setText(item);
455 |
456 | ObservableList nodes = statusText.getChildren();
457 |
458 | Text time = new Text();
459 | time.setId("status-time"); // Defined in CSS
460 | time.setText(new SimpleDateFormat("HH.mm.ss: ").format(new java.util.Date()));
461 |
462 | Text txt = new Text();
463 | txt.setText(status + SpeedGuide.NEWLINE);
464 |
465 | switch (indicator)
466 | {
467 | case REQUEST:
468 | txt.setId("status-request"); // Defined in CSS
469 | break;
470 | case RESPONSE_SUCCESS:
471 | txt.setId("status-response-success"); // Defined in CSS
472 | txt.setFill(Color.GREEN);
473 | break;
474 | case RESPONSE_ERROR:
475 | txt.setId("status-response-error"); // Defined in CSS
476 | txt.setFill(Color.RED);
477 | break;
478 | }
479 |
480 | nodes.add(0, txt);
481 | nodes.add(0, time);
482 | }
483 | });
484 | }
485 | }
486 |
--------------------------------------------------------------------------------
/src/main/java/com/lseg/ema/example/gui/SpeedGuideConsumer.java:
--------------------------------------------------------------------------------
1 | package com.lseg.ema.example.gui;
2 |
3 | import java.util.List;
4 | import java.util.logging.ConsoleHandler;
5 | import java.util.logging.Handler;
6 | import java.util.logging.Logger;
7 |
8 | import com.lseg.ema.example.gui.SpeedGuide.StatusIndicator;
9 | import com.lseg.ema.example.gui.view.SpeedGuideViewController;
10 | import com.lseg.ema.example.gui.view.SpeedGuideConnection.RTO_V1Connection;
11 | import com.lseg.ema.example.gui.view.SpeedGuideConnection.RTO_V2Connection;
12 | import com.lseg.ema.example.gui.view.SpeedGuideConnection.ADSConnection;
13 | import com.refinitiv.ema.access.AckMsg;
14 | import com.refinitiv.ema.access.ElementEntry;
15 | import com.refinitiv.ema.access.ElementList;
16 | import com.refinitiv.ema.access.EmaFactory;
17 | import com.refinitiv.ema.access.FilterEntry;
18 | import com.refinitiv.ema.access.FilterList;
19 | import com.refinitiv.ema.access.GenericMsg;
20 | import com.refinitiv.ema.access.Map;
21 | import com.refinitiv.ema.access.MapEntry;
22 | import com.refinitiv.ema.access.Msg;
23 | import com.refinitiv.ema.access.OmmConsumer;
24 | import com.refinitiv.ema.access.OmmConsumerClient;
25 | import com.refinitiv.ema.access.OmmConsumerConfig;
26 | import com.refinitiv.ema.access.OmmConsumerErrorClient;
27 | import com.refinitiv.ema.access.OmmConsumerEvent;
28 | import com.refinitiv.ema.access.OmmException;
29 | import com.refinitiv.ema.access.RefreshMsg;
30 | import com.refinitiv.ema.access.ReqMsg;
31 | import com.refinitiv.ema.access.ServiceEndpointDiscovery;
32 | import com.refinitiv.ema.access.ServiceEndpointDiscoveryClient;
33 | import com.refinitiv.ema.access.ServiceEndpointDiscoveryEvent;
34 | import com.refinitiv.ema.access.ServiceEndpointDiscoveryInfo;
35 | import com.refinitiv.ema.access.ServiceEndpointDiscoveryOption;
36 | import com.refinitiv.ema.access.ServiceEndpointDiscoveryResp;
37 | import com.refinitiv.ema.access.StatusMsg;
38 | import com.refinitiv.ema.access.UpdateMsg;
39 | import com.refinitiv.ema.access.DataType.DataTypes;
40 | import com.refinitiv.ema.rdm.EmaRdm;
41 |
42 | /**
43 | * The SpeedGuideConsumer class
44 | *
45 | */
46 | public class SpeedGuideConsumer implements Runnable
47 | {
48 | public static final String DEFAULT_REGION = "";
49 | public ConsumerClient _consumerClient = new ConsumerClient();
50 | protected OmmConsumer _consumer = null;
51 | private boolean _launchConsumer = false;
52 |
53 | private String _region = DEFAULT_REGION;
54 | private String _service;
55 |
56 | // ADS connection parameters
57 | private ADSConnection _adsConnection;
58 |
59 | // RTO V1 connection parameters
60 | private RTO_V1Connection _rto_v1_connection;
61 |
62 | // RTO V2 connection parameters
63 | private RTO_V2Connection _rto_v2_connection;
64 |
65 | private boolean _debug = false;
66 | private StatusLogHandler m_statusLogHandler;
67 |
68 | private SpeedGuideViewController m_viewController;
69 |
70 | public void setViewController(SpeedGuideViewController viewController) {
71 | m_viewController = viewController;
72 | m_statusLogHandler.setViewController(viewController);
73 | _consumerClient.setViewController(viewController);
74 | m_viewController.addRegion(_region); // Default Region as defined by EMA
75 | }
76 |
77 | public SpeedGuideConsumer() {
78 | setLogHandler();
79 | _consumerClient.setConsumer(this);
80 | }
81 |
82 | public void setLogHandler()
83 | {
84 | final Logger parentLogger = Logger.getAnonymousLogger().getParent();
85 | m_statusLogHandler = new StatusLogHandler();
86 | parentLogger.addHandler(m_statusLogHandler);
87 | }
88 |
89 | public void setDebug(boolean debug)
90 | {
91 | _debug = debug;
92 | _consumerClient.setDebug(debug);
93 |
94 | if (!debug)
95 | {
96 | final Logger parentLogger = Logger.getAnonymousLogger().getParent();
97 |
98 | // Disable console logger if debug mode not turned on
99 | for (Handler handler : parentLogger.getHandlers())
100 | {
101 | if ( handler instanceof ConsoleHandler )
102 | parentLogger.removeHandler(handler);
103 | }
104 | }
105 | }
106 |
107 | public boolean isConnectionRTO()
108 | {
109 | return _rto_v1_connection != null || _rto_v2_connection!= null;
110 | }
111 |
112 | public void defineADSConsumer(ADSConnection connectionParams)
113 | {
114 | _adsConnection = connectionParams;
115 |
116 | String userName = (connectionParams._user.isEmpty() ? "" : "user");
117 | if (_debug) System.out.println("Connecting to ADS @ host:[" +
118 | connectionParams._host + "] service: [" +
119 | connectionParams._service == null ? "" : connectionParams._service);
141 | System.out.println("Using keystore file: " + connectionParams._keystoreFile);
142 | }
143 |
144 | _adsConnection = null;
145 | _rto_v2_connection = null;
146 | _launchConsumer = true;
147 | }
148 |
149 | public void defineRTO_V2Consumer(RTO_V2Connection connectionParams)
150 | {
151 | _rto_v2_connection = connectionParams;
152 |
153 | if (_debug)
154 | {
155 | System.out.println("Connecting to Real-Time -- Optimized @ clientID:[" + connectionParams._clientId +
156 | "] clientSecret: [" + connectionParams._clientSecret + "] service: [" +
157 | connectionParams._service == null ? "" : connectionParams._service);
158 | System.out.println("Using keystore file: " + connectionParams._keystoreFile);
159 | }
160 |
161 | _adsConnection = null;
162 | _rto_v1_connection = null;
163 | _launchConsumer = true;
164 | }
165 |
166 | public void defineNewRegion()
167 | {
168 | // We simply notify our run loop to attempt to connect - this is because we selected a new region
169 | _launchConsumer = true;
170 | }
171 |
172 | public void setRegion(String region)
173 | {
174 | if (region != null) {
175 | _region = region.trim();
176 | m_viewController.addRegion(_region);
177 | }
178 | }
179 |
180 | public String getRegion()
181 | {
182 | return(_region);
183 | }
184 |
185 | public void setService(String service)
186 | {
187 | _service = service.trim();
188 | }
189 |
190 | public String getService()
191 | {
192 | return(_service);
193 | }
194 |
195 | private void connectConsumer()
196 | {
197 | if (_adsConnection != null)
198 | connectADS();
199 | else if (_rto_v1_connection != null || _rto_v2_connection != null)
200 | connectRTO();
201 | }
202 |
203 | private void connectADS()
204 | {
205 | _launchConsumer = false;
206 |
207 | String connectStr = "Attempting to connect ADS: [" + _adsConnection._host + "]";
208 | m_viewController.setConnection(false, true, "Connection to ADS in progress...");
209 | m_viewController.updateStatus(connectStr, StatusIndicator.REQUEST);
210 | OmmConsumerConfig config = EmaFactory.createOmmConsumerConfig().host(_adsConnection._host);
211 |
212 | // We have to set the user/appId/position in this fashion because a blank value may not default correctly.
213 | if ( !_adsConnection._user.isEmpty() ) config.username(_adsConnection._user);
214 | if ( !_adsConnection._appId.isEmpty() ) config.applicationId(_adsConnection._appId);
215 | if ( !_adsConnection._position.isEmpty() ) config.position(_adsConnection._position);
216 | if ( !_adsConnection._service.isEmpty() ) _service = _adsConnection._service;
217 |
218 | // Register Consumer
219 | registerConsumer(config);
220 | }
221 |
222 | private void connectRTO()
223 | {
224 | _launchConsumer = false;
225 |
226 | // Determine if something is already running...
227 | if (_consumer != null) {
228 | _consumer.uninitialize();
229 | _consumer = null;
230 | }
231 |
232 | String connectStr = "Attempting to connect Real-Time -- Optimized";
233 | m_viewController.setConnection(false, true, "Connection to Real-Time -- Optimized in progress...");
234 | m_viewController.updateStatus(connectStr, StatusIndicator.REQUEST);
235 |
236 | // Configuration
237 | // Because we are supporting 2 RTO Authentication mechanisms, we'll need to prioritize which one to use
238 | // if both are populated with data.
239 | OmmConsumerConfig config;
240 | if (_rto_v2_connection != null) {
241 | config = EmaFactory.createOmmConsumerConfig().clientId(_rto_v2_connection._clientId)
242 | .clientSecret(_rto_v2_connection._clientSecret);
243 | }
244 | else {
245 | config = EmaFactory.createOmmConsumerConfig().username(_rto_v1_connection._machineId)
246 | .password(_rto_v1_connection._password)
247 | .clientId(_rto_v1_connection._appKey);
248 | }
249 |
250 | Map configDb = EmaFactory.createMap();
251 |
252 | // Service discovery - we query ERT for the available services
253 | ServiceEndpointDiscovery serviceDiscovery = EmaFactory.createServiceEndpointDiscovery();
254 |
255 | ServiceEndpointDiscoveryOption options = EmaFactory.createServiceEndpointDiscoveryOption();
256 |
257 | String keyStoreFile;
258 | String keyStorePasswd;
259 | if (_rto_v2_connection != null) {
260 | options.clientId(_rto_v2_connection._clientId)
261 | .clientSecret(_rto_v2_connection._clientSecret);
262 | keyStoreFile = _rto_v2_connection._keystoreFile;
263 | keyStorePasswd = _rto_v2_connection._keystorePasswd;
264 | if ( !_rto_v2_connection._service.isEmpty() ) _service = _rto_v2_connection._service;
265 | }
266 | else {
267 | options.username(_rto_v1_connection._machineId)
268 | .password(_rto_v1_connection._password)
269 | .clientId(_rto_v1_connection._appKey);
270 | keyStoreFile = _rto_v1_connection._keystoreFile;
271 | keyStorePasswd = _rto_v1_connection._keystorePasswd;
272 | if ( !_rto_v1_connection._service.isEmpty() ) _service = _rto_v1_connection._service;
273 | }
274 |
275 | // Capture service discovery events
276 | serviceDiscovery.registerClient(
277 | options.transport(ServiceEndpointDiscoveryOption.TransportProtocol.TCP),
278 | //.proxyHostName(proxyHostName)
279 | //.proxyPort(proxyPort)
280 | //.proxyUserName(proxyUserName)
281 | //.proxyPassword(proxyPassword)
282 | //.proxyDomain(proxyDomain)
283 | //.proxyKRB5ConfigFile(proxyKrb5Configfile)
284 | new ServiceEndpointDiscoveryClient() {
285 | public void onSuccess(ServiceEndpointDiscoveryResp serviceEndpointResp, ServiceEndpointDiscoveryEvent event)
286 | {
287 | if (_debug)
288 | System.out.println("Service Discovery:\n" + serviceEndpointResp); // dump service discovery endpoints
289 |
290 | for(int index = 0; index < serviceEndpointResp.serviceEndpointInfoList().size(); index++)
291 | {
292 | ServiceEndpointDiscoveryInfo location = serviceEndpointResp.serviceEndpointInfoList().get(index);
293 | m_viewController.addRegion(getRegion(location.locationList()));
294 | }
295 | m_viewController.setRTOConnection(true);
296 | }
297 |
298 | private String getRegion(List locationList) {
299 | // Just need to retrieve the 1st element to map region
300 | String region = locationList.get(0);
301 | if (region.matches(".*[a-z]$"))
302 | return region.substring(0, region.length()-1);
303 | else
304 | return region;
305 | }
306 |
307 | public void onError(String errorText, ServiceEndpointDiscoveryEvent event)
308 | {
309 | m_viewController.updateStatus("Failed to query service discovery. [" + errorText + "]", StatusIndicator.RESPONSE_ERROR);
310 | m_viewController.setConnection(false, false, "Connection Failed.");
311 | }
312 | });
313 |
314 | // Create an in-memory configuration (only if we were able to connect and retrieve and endpoint)
315 | createProgramaticConfig(configDb);
316 | config.config(configDb);
317 | config.consumerName("Consumer_1");
318 |
319 | // Define keystore files used for encryption
320 | config.tunnelingKeyStoreFile(keyStoreFile);
321 | config.tunnelingKeyStorePasswd(keyStorePasswd);
322 |
323 | // Register Consumer
324 | registerConsumer(config);
325 | }
326 |
327 | // registerConsumer
328 | // Creates our OMM Consumer to capture directory and market data requests
329 | private void registerConsumer(OmmConsumerConfig config)
330 | {
331 | // Create our consumer of market data
332 | _consumer = EmaFactory.createOmmConsumer(config,
333 | new OmmConsumerErrorClient() {
334 | public void onInvalidHandle(long handle, String text) {
335 | m_viewController.updateStatus("OnInvalidHandle: " + text, StatusIndicator.RESPONSE_ERROR);
336 | }
337 |
338 | public void onInvalidUsage(String text) {
339 | if (m_viewController.isConnecting()) {
340 | String msg = "Connection Failed. " + text;
341 | m_viewController.setConnection(false, false, msg);
342 | }
343 | }
344 | });
345 |
346 | ReqMsg reqMsg = EmaFactory.createReqMsg();
347 |
348 | // Register interest in the Directory information to capture the list of Services for this connection
349 | _consumer.registerClient(reqMsg.domainType(EmaRdm.MMT_DIRECTORY),
350 | new OmmConsumerClient() {
351 | public void onGenericMsg(GenericMsg genericMsg, OmmConsumerEvent consumerEvent){}
352 | public void onAckMsg(AckMsg ackMsg, OmmConsumerEvent consumerEvent){}
353 | public void onAllMsg(Msg msg, OmmConsumerEvent consumerEvent){}
354 | public void onRefreshMsg(RefreshMsg refreshMsg, OmmConsumerEvent consumerEvent) {
355 | decode(refreshMsg.payload().map());
356 | }
357 | public void onUpdateMsg(UpdateMsg updateMsg, OmmConsumerEvent consumerEvent){}
358 | public void onStatusMsg(StatusMsg statusMsg, OmmConsumerEvent consumerEvent){}
359 |
360 | void decode(ElementList elementList)
361 | {
362 | for (ElementEntry elementEntry : elementList)
363 | {
364 | if ( elementEntry.name().equals("Name") )
365 | {
366 | String service = elementEntry.ascii().toString();
367 |
368 | // Set the default SERVICE based on the 1st one found - usually ELEKTRON_DD
369 | if (_service == null)
370 | _service = service;
371 |
372 | m_viewController.addService(service);
373 | m_viewController.updateStatus("Discovered Service: " + service, StatusIndicator.RESPONSE_SUCCESS);
374 | break;
375 | }
376 | }
377 | }
378 |
379 | void decode(Map map)
380 | {
381 | for(MapEntry mapEntry : map)
382 | {
383 | switch (mapEntry.loadType())
384 | {
385 | case DataTypes.FILTER_LIST :
386 | decode(mapEntry.filterList());
387 | break;
388 | }
389 | }
390 | }
391 |
392 | void decode(FilterList filterList)
393 | {
394 | for(FilterEntry filterEntry : filterList)
395 | {
396 | switch (filterEntry.loadType())
397 | {
398 | case DataTypes.ELEMENT_LIST :
399 | decode(filterEntry.elementList());
400 | break;
401 | case DataTypes.MAP :
402 | decode(filterEntry.map());
403 | break;
404 | }
405 | }
406 | }
407 | });
408 | _consumer.registerClient(reqMsg.domainType(EmaRdm.MMT_LOGIN),
409 | new OmmConsumerClient() {
410 | public void onGenericMsg(GenericMsg genericMsg, OmmConsumerEvent consumerEvent){}
411 | public void onAckMsg(AckMsg ackMsg, OmmConsumerEvent consumerEvent){}
412 | public void onAllMsg(Msg msg, OmmConsumerEvent consumerEvent) {}
413 | public void onRefreshMsg(RefreshMsg refreshMsg, OmmConsumerEvent consumerEvent)
414 | {
415 | m_viewController.updateStatus(refreshMsg.state().statusText(), StatusIndicator.RESPONSE_SUCCESS);
416 | m_viewController.setConnection(true, false, "");
417 | subscribe("REFINITIV");
418 | }
419 | public void onUpdateMsg(UpdateMsg updateMsg, OmmConsumerEvent consumerEvent){}
420 | public void onStatusMsg(StatusMsg statusMsg, OmmConsumerEvent consumerEvent){}
421 | });
422 | }
423 |
424 | private void createProgramaticConfig(Map configDb)
425 | {
426 | Map elementMap = EmaFactory.createMap();
427 | ElementList elementList = EmaFactory.createElementList();
428 | ElementList innerElementList = EmaFactory.createElementList();
429 |
430 | innerElementList.add(EmaFactory.createElementEntry().ascii("Channel", "Channel_1"));
431 |
432 | elementMap.add(EmaFactory.createMapEntry().keyAscii("Consumer_1", MapEntry.MapAction.ADD, innerElementList));
433 | innerElementList.clear();
434 |
435 | elementList.add(EmaFactory.createElementEntry().map("ConsumerList", elementMap));
436 | elementMap.clear();
437 |
438 | configDb.add(EmaFactory.createMapEntry().keyAscii("ConsumerGroup", MapEntry.MapAction.ADD, elementList));
439 | elementList.clear();
440 |
441 | innerElementList.add(EmaFactory.createElementEntry().ascii("ChannelType", "ChannelType::RSSL_ENCRYPTED"));
442 |
443 | if ( !_region.equals(DEFAULT_REGION) )
444 | innerElementList.add(EmaFactory.createElementEntry().ascii("Location", _region));
445 |
446 | innerElementList.add(EmaFactory.createElementEntry().intValue("EnableSessionManagement", 1));
447 | innerElementList.add(EmaFactory.createElementEntry().intValue("CompressionThreshold", 300));
448 |
449 | elementMap.add(EmaFactory.createMapEntry().keyAscii("Channel_1", MapEntry.MapAction.ADD, innerElementList));
450 | innerElementList.clear();
451 |
452 | elementList.add(EmaFactory.createElementEntry().map("ChannelList", elementMap));
453 | elementMap.clear();
454 |
455 | configDb.add(EmaFactory.createMapEntry().keyAscii("ChannelGroup", MapEntry.MapAction.ADD, elementList));
456 | }
457 |
458 | /**
459 | * Subscribe to RIC
460 | * @param ric
461 | */
462 |
463 | public void subscribe(String ric) {
464 | try {
465 | if (!ric.isEmpty())
466 | {
467 | if (_debug) System.out.println("Registering interest in item: " + ric);
468 | _consumer.registerClient(EmaFactory.createReqMsg().serviceName(_service)
469 | .name(ric)
470 | .interestAfterRefresh(false),
471 | _consumerClient);
472 | }
473 | } catch (OmmException excp) {
474 | m_viewController.updateStatus(excp.getMessage(), StatusIndicator.RESPONSE_ERROR);
475 | }
476 | }
477 |
478 | public void run()
479 | {
480 | while(true)
481 | {
482 | long time = (m_viewController.isConnected() ? 1000 : 10);
483 | try {
484 | if (_launchConsumer)
485 | connectConsumer();
486 |
487 | Thread.sleep(time);
488 | } catch(InterruptedException ie){}
489 | }
490 | }
491 | }
492 |
--------------------------------------------------------------------------------