Building Enterprise Message API Java Application with Gradle in a Simple way

Wasin Waeosri
Developer Advocate Developer Advocate

Introduction

Refinitiv Real-Time SDK (Java Edition) (RTSDK, formerly known as Elektron SDK) is a suite of modern and open source APIs that aim to simplify development through a strong focus on ease of use and standardized access to a broad set of Refinitiv proprietary content and services via the proprietary TCP connection named RSSL and proprietary binary message encoding format named OMM Message. The capabilities range from low latency/high-performance APIs right through to simple streaming Web APIs.

The SDK has been released on the Maven Central Repository to support the modern Java development life cycle since the RTSDK Java (formerly known as Elektron SDK) version 1.2. The Maven Central Repository supported also lets SDK compatibilities with the Java build automation tools like Gradle and Apache Maven. This helps Java developers to build RTSDK Java applications, manage its dependencies (Java Developers do not need to manually manage different versions of jar files anymore), and better collaboration in the team.

The RTSDK Java package comes with the Gradle build tool supported by default. However, the included Gradle configuration files are very complex for supporting the SDK multiple APIs (EMA and ETA) and use cases (Core API code, examples, ValueAdd package, etc). If developers want to use Gradle, the RTSDK Java's Gradle settings might not be a good starting point for them.

That is why I am creating this project to show how to use a simple Gradle configuration to work with the EMA Java API. Existing developers who are currently using Gradle also understand how to integrate RTSDK Java into their Gradle projects.

Note:

  • This article is based on EMA Java version 3.7.0 L1 (RTSDK Java Edition 2.1.0 L1) and Gradle version 7.3.3 (using Grooy DSL)

What is Gradle?

So, I will start off with what the Gradle is. Gradle is a multi-language open-source build automation tool. It helps developers and the team to organize the project structure, manage dependencies, and process development tasks such as running the application, packaging/publishing, testing, and much more. The tool is designed for multi-project builds, aims for high performance, supports multiple platforms (runs on the JVM), supports custom tasks and plugins, and fully IDE supported.

Gradle is the official build tool for the Android development platform.

Why you need build automation tool

Now let me turn to the reason the Java development teams need the build automation tool. The modern Java build automation tools help developers to automate the software build and project management processes including compiling, dependency manager, packing, and running tests.

If you want to manage the EMA Java application project manually (the old way), you need to manage total 26 jar files (as of April 2023), and developers need to add all jar files to the classpath when running the EMA Java application manually like the following example

    	
            $>java -cp .;%EMAJ_HOME%\Libs\ema-<version>.jar;%ETAJ_HOME%\Libs\eta-<version>.jar;%ETAJ_HOME%\Libs\etaValueAdd-<version>.jar;%ETAJ_HOME%\Libs\etaValueAddCache-<version>.jar;%ETAJ_HOME%\Libs\etajConverter-<version>.jar;%ETAJ_HOME%\Libs\ansipage-<version>.jar;%BINPAK%\Ema\Libs\apache\commons-configuration2-2.8.0.jar;%BINPAK%\Ema\Libs\apache\commons-lang3-3.9.jar;%BINPAK%\Ema\Libs\apache\commons-logging-1.2.jar;%BINPAK%\Ema\Libs\apache\commons-text-1.9.jar;%BINPAK%\Ema\Libs\xpp3-1.1.4c.jar;%BINPAK%\Eta\Libs\jackson-annotations-2.14.1.jar;%BINPAK%\Eta\Libs\jackson-core-2.14.1.jar;%BINPAK%\Eta\Libs\jackson-databind-2.14.1.jar;%BINPAK%\Eta\Libs\json-20210307.jar;%BINPAK%\Eta\Libs\lz4-java-1.8.0.jar;%BINPAK%\Eta\Libs\ApacheClient\commons-codec-1.11.jar;%BINPAK%\Eta\Libs\ApacheClient\httpclient-4.5.13.jar;%BINPAK%\Eta\Libs\ApacheClient\httpclient-cache-4.5.13.jar;%BINPAK%\Eta\Libs\ApacheClient\httpcore-4.4.13.jar;%BINPAK%\Eta\Libs\ApacheClient\httpcore-nio-4.4.13.jar;%BINPAK%\Eta\Libs\ApacheClient\httpmime-4.5.13.jar;%BINPAK%\Eta\Libs\jose4j\jose4j-0.9.1.jar;%BINPAK%\Eta\Libs\SLF4J\slf4j-1.7.32\slf4j-api-1.7.32.jar;%BINPAK%\Eta\Libs\SLF4J\slf4j-1.7.32\slf4j-jdk14-1.7.32.jar com.refinitiv.ema.examples.training.consumer.series100.ex100_MP_Streaming.Consumer
        
        
    

The example above is just for running the EMA Java application, the real development project needs to connect to more services, which means the project needs more jar files, configuration files, etc to manage. This makes the development project hard to set up and hard to collaborate among peers. Beside the jar file management, the team needs a clear project folder structure for easy organize.

The build automation tool can help simplify all of this complexity by helping team manage the project dependencies, and standardize project structure with a simple configuration setting as follows:

    	
            

// tag::dependencies[]

dependencies {

    // This dependency is used by the application.

    implementation 'com.refinitiv.ema:ema:3.7.0.0'

}

And it lets developers compile, run, and test the application with just a command.

    	
            $> gradlew run
        
        
    

Based on the example above, I think you get the idea of how the build automation tool organizes the project.

Prerequisite

Before I am going further, there is some prerequisite, dependencies, and libraries that the project is needed.

Java SDK

Firstly, you need Java SDK. Please check for the supported Java version from the API Compatibility Matrix page.

I am using the Open JDK version 11 in this project (as of April 2023).

Gradle

Next, the Gradle build automation tool. Please follow Gradle installation guide document.

Gradle Wrapper version 7.3.3

Basically, developers use the Gradle Wrapper to interact with the Gradle build. The Wrapper (gradlew and gradlew.bat) is a script that invokes a declared version of Gradle, downloading it beforehand if necessary.

This project uses the Gradle Wrapper version 7.3.3 (and above). You can check the current Gradle Wrapper version from the following command:

    	
            $> gradlew --version
        
        
    

If your Gradle Wrapper is older than version 7.3.3, you can update the Wrapper with the following command

    	
            $> gradlew wrapper --gradle-version=7.3.3
        
        
    

Access to the Refinitiv Real-Time Optimized

This project uses Customer Identity and Access Management (CIAM) (aka Version 2 Authentication) - Client Credentials Grant Model to connect to the Refinitiv Real-Time Optimized (RTO).

  • Note: the demo example supports Version 1 Authentication (Machine ID too).

Please contact your Refinitiv representative to help you with the RTO account and services.

Note: This is for the CloudConsumer example only.

Access to Refinitiv Real-Time Distribution System

Note: This is for the LocalConsumer example only. Please contact your Market Data team to help you with the Refinitiv Real-Time Distribution System (RTDS).

That covers the project's prerequisite.

Project Structure

Moving on to the Gradle project structure. Gradle is designed for multi-projects development. A recommended structure consists of one root project, and one or more subprojects (the RTSDK Java uses the same structure too). This project folder diagram is as follows:

    	
            

.

├── LICENSE.md

├── README.md

├── ema_app

│   ├── EmaConfig.xml

│   ├── build.gradle

│   ├── etc

│   │   └── ...

│   └── src

│       ├── main

│       │   ├── java

│       │   │   └── com

│       │   │       └── refinitiv

│       │   │           └── ema

│       │   │               └── examples

│       │   │                   ├── cloudconsumer

│       │   │                   │   └── Consumer.java

│       │   │                   └── localconsumer

│       │   │                       └── Consumer.java

│       │   └── resources

│       │       └── logback.xml

│       └── test

│              └── resources

│                 └── ...

├── gradle

│   └── wrapper

│       ├── gradle-wrapper.jar

│       └── gradle-wrapper.properties

├── gradlew

├── gradlew.bat

└── settings.gradle

  • <root>/settings.gradle: Defined the subprojects to include.
  • <root>/<subproject>/build.gradle: Subproject configuration
  • <root>/<subproject>/src/main/java: Application/Library sources
  • <root>/<subproject>/src/main/resources: Application/Library resources
  • <root>/<subproject>/src/test/java: Test sources
  • <root>/<subproject>/src/test/resources: Test resources
  • <root>/<subproject>/gradle/: Gradle Wrapper (generated from Gradle build)
  • <root>/<subproject>/gradlew: Gradle Wrapper start script (generated from Gradle build)
  • <root>/<subproject>/gradlew.bat: Gradle Wrapper start script (generated from Gradle build)
  • <root>/LICENSE.txt: Project's license
  • <root>/README.txt: Project's readme

If you are familiar with Maven, the Gradle project layout is mostly identical to the Maven project.

For more detail on the Gradle project layout, please check the following resources:

With Gradle, the project always has a blueprint for the folder structure.

The settings.gradle file

My next point is the root project setting. The root project does not have a Gradle build file, it contains only settings.gradle file that defines the subprojects to include per Gradle build. The file is generated by the Gradle 'init' task automatically.

This file is actually a Groovy Domain-Specific Language (DSL) in the following format:

    	
            

rootProject.name = '<Project name>'

include('sub_project_1')

include('sub_project_2')

I am using the following setting for this project:

    	
            

rootProject.name = 'ema_app'

include('ema_app')

Note: If the project uses Kotlin DSL, this file is settings.gradle.kts.

That’s all I have to say about the settings.gradle file.

Gradle build file setting for EMA Java

That brings us to the subproject configuration. Each subproject contains the build.gradle or build.gradle.kts file. It is the project's configuration. The file uses Groovy (default option) and Kotlin DSL format.

The file contains all project configurations such as project type, Application main class, Java compilation options, etc. The equivalent file in Maven is the pom.xml file.

Example:

    	
            

plugins {

    id 'java'

    id 'application'

   

}

 

compileJava {

    options.release = 11

}

 

version = '1.0'

 

application {

    // Define the main class for the application

    mainClassName = 'com.refinitiv.ema.examples.localconsumer.Consumer'

}

The brief information of each build.gradle configuration function is as follows:

  • plugins: Set the project type to Gradle for applying specific features (like compiling Java Code). This is a Java project that creates an executable JVM application, so I am setting java and application plugins
  • compileJava: Set the compiler option. I am setting options.release = 11 for targeting the compiled class(es) to compatible with Java 11
  • version: Set the project version
  • application: Set the application plugin properties. I am setting the project main class as com.refinitiv.ema.examples.localconsumer.Consumer which is based on the EMA Java Consumer 100 example.

You can specify the following EMA Java application dependencies in Gradle build.gradle file. The EMA Java is the message-level API built on top of the ETA Java (Transport API), Gradle can automatically pull all dependency artifacts within Maven central for the application.

    	
            

plugins {

    id 'java'

    id 'application'  

}

...

repositories {

    mavenCentral()

}

 

dependencies {

    // This dependency is used by the application.

    implementation 'com.refinitiv.ema:ema:3.7.0.0'

}

The repositories function specifies where to look for the module that we declare as dependencies. The dependencies function specifies the libraries' IDs and the implementation property means the project uses EMA for both compilation and runtime.

If you want to add more depedencies, just add them to the dependencies function as the following example for the Logback framework.

    	
            

// tag::dependencies[]

dependencies {

    // This dependency is used by the application.

    // EMA Java

    implementation 'com.refinitiv.ema:ema:3.7.0.0'

    // Logback

    implementation 'ch.qos.logback:logback-classic:1.4.6'

    implementation 'ch.qos.logback:logback-core:1.4.6'

}

Please see more detail on the following pages:

That’s all I have to say about the subproject configuration and dependencies.

Build and Run the Main Application with Gradle

You can run the build task to compile all projects via the following command:

    	
            gradlew build
        
        
    

The class files will be available in the subproject's build folder by default.

Note: Gradle encourages developers to interact with Gradle via the Gradle Wrapper.

Unlike Maven, developers can build and run the main application (the class that has been set via the application.mainClassName function above) with Gradlde run task directly without interacting with class or jar files.

    	
            gradlew run
        
        
    

Example:

    	
            

$>gradlew run

 

> Task :ema_app:run

17:26:49.611 [main] INFO com.refinitiv.ema.access.OmmConsumerImpl -- loggerMsg

    ClientName: ChannelCallbackClient

    Severity: Info

    Text:    Received ChannelUp event on channel Channel_1

        Instance Name Consumer_1_1

        Component Version ads3.6.3.L1.linux.rrg 64-bit

loggerMsgEnd



RefreshMsg

    streamId="5"

    domain="MarketPrice Domain"

    solicited

    RefreshComplete

    state="Open / Ok / None / ''"

    itemGroup="00 01"

    permissionData="03 01 01 36 3c"

    name="/THB="

    nameType="1"

    serviceId="10001"

    serviceName="ELEKTRON_DD"

    Payload dataType="FieldList"

        FieldList FieldListNum="99" DictionaryId="1"

            FieldEntry fid="1" name="PROD_PERM" dataType="UInt" value="363"

            FieldEntry fid="2" name="RDNDISPLAY" dataType="UInt" value="153"

            FieldEntry fid="3" name="DSPLY_NAME" dataType="Rmtes" value="CIMB THAI BK B/d"

            FieldEntry fid="5" name="TIMACT" dataType="Time" value="09:56:00:000:000:000"

            FieldEntry fid="11" name="NETCHNG_1" dataType="Real" value="0.23"

            FieldEntry fid="12" name="HIGH_1" dataType="Real" value="34.46"

            FieldEntry fid="13" name="LOW_1" dataType="Real" value="34.17"

            ....

How to parse parameters to the application.

My current main class is com.refinitiv.ema.examples.localconsumer.Consumer which is based on the EMA Java Consumer ex100_MP_Streaming example. The application connects and consumes real-time streaming data with the service ELEKTRON_DD from the RTDS or Interactive Provider application in the same machine by default. However, I have modified it to receive the server IP Address/Hostname, RSSL Port, Service Name, Item Name, and DACS username via command-line arguments. So, how can developers parse parameters to the application via the gradlew run command?

You can pass parameters to the run task with args= option as follows:

    	
            gradlew run --args="-service IDN_RDF -itemName AAPL.O"
        
        
    

To clean up the build, you can just run a gradlew clean command to can delete the contents of the build directory.

Please find more detail about the Gradle command-line interface from the Gradle page.

Build and Run Other Java Tasks

Running the main application with the gradlew run command is convenient. However, developers may need to run multiple applications to start or test other services at the same time. It is not practical to keep changing the main class in application.mainClassName function of a build.gradle file. So, how can we run multiple applications with less modification?

Fortunately, Gradle lets developers create tasks for various propose, including running others Java classes. The best example is the official RTSDK Java build.gradle files that support various SDK samples via Gradle task such as gradlew runconsumer100 gradlew runiprovider180 gradlew runVAConsumer etc.

While RTSDK Java's build.gradle uses a complex script to support multiple tasks dynamically, this project uses a simple custom task for running Java applications as follows

    	
            

task runCloudConsumer(type: JavaExec) {

 

    dependsOn('compileJava')

    classpath = sourceSets.main.runtimeClasspath

   

    mainClass = 'com.refinitiv.ema.examples.cloudconsumer.Consumer'

}

The runCloudConsumer task is defined as type JavaExec for executing a Java application in a child process. Developers can set the classpath, application class, and task properties in the task function.

  • classpath: The classpath for executing the main class.
  • mainClass: The fully qualified name of the Main class to be executed. I am using the com.refinitiv.ema.examples.cloudconsumer.Consumer which is based on EMA Java Consumer ex113_MP_SessionMgmt example.
  • dependsOn('compileJava'): The method is for setting this task to compile the project before running the class.

You can run the Gradle task with the gradlew [taskName...] [--option-name] command. Please noted that it supports the args option too.

Example:

    	
            

$>gradlew runCloudConsumer --args="-clientId $RTO_CLIENTID_V2 -clientSecret $RTO_CLIENTSECRET -itemName /THB="

 

> Task :ema_app:runCloudConsumer

16:22:56.111 [main] INFO com.refinitiv.ema.access.OmmConsumerImpl -- loggerMsg

    ClientName: ChannelCallbackClient

    Severity: Info

    Text:    Received ChannelUp event on channel Channel_4

        Instance Name Consumer_4_1

        Component Version ads3.5.4.E1.linux.rrg 64-bit

loggerMsgEnd



RefreshMsg

    streamId="5"

    domain="MarketPrice Domain"

    solicited

    RefreshComplete

    state="Open / Ok / None / ''"

    itemGroup="00 08"

    permissionData="03 01 01 36 3c"

    name="/THB="

    nameType="1"

    serviceId="257"

    serviceName="ELEKTRON_DD"

    Payload dataType="FieldList"

        FieldList FieldListNum="99" DictionaryId="1"

            FieldEntry fid="1" name="PROD_PERM" dataType="UInt" value="363"

            FieldEntry fid="2" name="RDNDISPLAY" dataType="UInt" value="153"

            FieldEntry fid="3" name="DSPLY_NAME" dataType="Rmtes" value="STONEX GROUP G/d"

            FieldEntry fid="5" name="TIMACT" dataType="Time" value="08:51:00:000:000:000"

            FieldEntry fid="11" name="NETCHNG_1" dataType="Real" value="0.00"

            FieldEntry fid="12" name="HIGH_1" dataType="Real" value="34.15"

            FieldEntry fid="13" name="LOW_1" dataType="Real" value="34.05"

            FieldEntry fid="19" name="OPEN_PRC" dataType="Real" value="34.06"

            FieldEntry fid="21" name="HST_CLOSE" dataType="Real" value="34.09"

            FieldEntry fid="22" name="BID" dataType="Real" value="34.09"

            FieldEntry fid="25" name="ASK" dataType="Real" value="34.14"

...

Please find more detail on the following resources:

That covers how to run the applications and tasks.

Building Jar file

Turning to how to distribute the application. You can run the Gradle gradlew jar command to compile the project and build the applications jar file. The jar files will be available in the <root>/<subproject>/build/lib folder by default.

However, this newly built jar file contains only the application class files, so you need to set the Java classpath to all required RTSDK jar files which store somewhere in your Gradle repository folder to run this jar file. To avoid this problem, you can customize the Jar task to build the project and RTSDK library into a single-all-dependencies jar file as follows:

    	
            

jar {

     manifest {

        attributes "Main-Class": 'com.refinitiv.ema.examples.localconsumer.Consumer'

    }

 

    from {

        configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }

    }

 

    archiveBaseName = 'EMA_Java_Gradle_all'

    archiveVersion =  version

    duplicatesStrategy(DuplicatesStrategy.EXCLUDE)

}

Once you have run the Gradle gradlew jar command, Gradel builds a single-all-dependencies jar file named EMA_Java_Gradle_all-1.0.jar in the <root>/<subproject>/build/lib folder.

That covers how to create a jar file.

Running the demo applications

My next point is how to run the demo applications. Please see the instructions for each scenario below.

Running the Cloud Consumer application

Please contact your Refinitiv representative to help you to access the RTO account and services.

To run the Cloud example, open the project folder in the command prompt and then run the following command:

Version 1 Authentication:

    	
            gradlew runCloudConsumer --args="-username $RTO_MACHINEID -password $RTO_PASSWORD -clientId $RTO_CLIENTID -itemName $RIC"
        
        
    

Version 2 Authentication:

    	
            gradlew runCloudConsumer --args="-clientId $RTO_CLIENTID_V2 -clientSecret $CLIENTSECRET -itemName $RIC"
        
        
    

Running the Local Consumer application

Please contact your Market data team to help you to access the RTDS account and services.

To run the Local example, open the project folder in the command prompt and then run the following command:

    	
            gradlew run --args="-service $SERVICE_NAME -itemName $RIC"
        
        
    

Note: The default service name is ELEKTRON_DD.

Conclusion

Now we come to the conclusion of this EMA Java and Gradle article. The RTSDK Java comes with Gradle supported by default. The library is also available in Maven central repository. This makes Java developers can implement the Real-Time application with Gradle or other the build automation tools like Apache Maven, or even the dependency manager tool like Apache Ivy. The tool helps Java developers reduce the complexity of maintaining jar file dependencies, standardized project structure, easily manage the development environment, and support various build processes that match developers' workflow.

When compared to Maven, the Gradle advantages are highly customizable builds, better performance/faster build time, and support for multi-project builds, and developers may prefer Groovy/Kotlin DSL style configuration over the XML file like Maven.

In contrast, the Maven is easier to learn when compared with the Gradle, and has larger documents and resources from both official and user-based websites.

Before I finish, let me just say it is based on the developers' preferences to choose Gradle or Maven (or others) as a main build tool. If the tool supports the Maven central repository, developers can use the RTSDK Java with their projects.

References

For further details, please check out the following resources:

For any questions related to this article or the RTSDK page, please use the Developer Community Q&A Forum.