This article walks through the process of installing, provisioning, and running a subset of the samples that are included with the OpenDXL Java Client.
The samples examined in this article demonstrate how to use the two distinct messaging models that are supported by DXL. A publish/subscribe event-based model and a service-based model with point-to-point (request/response) communication.
Client Installation and Provisioning
The following steps detail how to install and provision the OpenDXL Java Client:
- Download the latest version of the OpenDXL Java Client
- Extract the release .zip (Windows) or .tar (Linux) file
- Follow the steps to provision the client for use with the samples
- The dxlclient.config file located in the sample sub-directory of the Java DXL SDK must be populated.
- The steps will vary based on the type of DXL fabric being connected to
Event-based Communication
The DXL fabric supports an event-based communication model. This model is typically referred to as “publish/subscribe” wherein clients register interest by subscribing to a particular topic and publishers periodically send events to that topic. The event is delivered by the DXL fabric to all of the currently subscribed clients for the topic. Therefore, a single event sent can reach multiple clients (one-to-many). It is important to note that in this model the client passively receives events when they are sent by a publisher.
For example, McAfee Advanced Threat Defense (ATD) servers send events to the topic /mcafee/event/atd/file/report when they have successfully determined the reputation of a submitted file. Any clients currently subscribed to this topic will receive the report and can take immediate action.
Run Sample
A "basic event sample" is included with the OpenDXL Java Client SDK that demonstrates sending and receiving 1000 events via the Java client.
To run the sample execute the following from the root directory of the extracted OpenDXL Java Client SDK:
sample\runsample sample.basic.EventSample
Sample Output
The output should appear similar to the following (1000 events are received and timing information is displayed).
Code Details
This section walks through the different portions of code that comprise the "basic event sample" in detail. Full code for the sample is available within the extracted SDK or via the OpenDXL Java Client GitHub Repository.
- /** The topic to publish to */
- private static final String EVENT_TOPIC = "/isecg/sample/basicevent";
- /** The total number of events to send */
- private static final int TOTAL_EVENTS = 1000;
- /** Lock used to protect changes to counter */
- private static final Lock eventCountLock = new ReentrantLock();
- /** Condition used to protect changes to counter */
- private static final Condition eventCountCondition = eventCountLock.newCondition();
- /** The count of events that have been received */
- private static int eventCount = 0;
As shown above, several variables are defined in the header of the "basic event sample":
-
EVENT_TOPIC: The topic that the events will be sent (and received) on:
- /isecg/sample/basicevent
- TOTAL_EVENTS: The total number of events to send (1000)
- eventCount: Tracks the number of events that have been received (lock and condition are used for concurrent access)
The next portion of code (shown above) creates the DXL client. The first step is to create a DxlClientConfig object that contains the information necessary to connect to a DXL fabric. In this particular case, a sample-specific common method (Common.getClientConfig) is invoked which ultimately invokes the DxlClientConfig.createDxlConfigFromFile static method which creates a configuration object from a file.
The next line of code creates a DxlClient object by passing the previously created configuration object to its constructor. It is also important to note that the client is created using a try-with-resource block. While not necessary, the try-with-resource block provides a simple way to control the lifetime of the client object and will automatically clean up any client-related resources when the client block ends (you do not need to explicitly call disconnect or close).
In the final line of code above, the connect method is invoked to connect to the DXL fabric.
- // Create and add event listener
- final EventCallback myEventCallback =
- event -> {
- try {
- System.out.println("Received event: "
- + new String(event.getPayload(), Message.CHARSET_UTF8));
- } catch (UnsupportedEncodingException ex) {
- ex.printStackTrace();
- }
- eventCountLock.lock();
- try {
- eventCount++;
- eventCountCondition.signalAll();
- } finally {
- eventCountLock.unlock();
- }
- };
- client.addEventCallback(EVENT_TOPIC, myEventCallback);
The next portion of code (shown above) registers an event callback that will receive events that are published to the /isecg/sample/basiceventtopic.
The event callback function is registered with the client via the addEventCallback method. This method will be invoked for each event that is sent to the DXL fabric for topics that are associated with the callback. The main portion of this method displays the payload of the event received and increments the total count of events.
- // Record the start time
- final long startTime = System.currentTimeMillis();
- // Loop and send the events
- for (int count = 0; count < TOTAL_EVENTS; count++) {
- final Event event = new Event(EVENT_TOPIC);
- event.setPayload(Integer.toString(count).getBytes(Message.CHARSET_UTF8));
- client.sendEvent(event);
- }
- // Wait until all events have been received
- System.out.println("Waiting for events to be received...");
- eventCountLock.lock();
- try {
- while (eventCount < TOTAL_EVENTS) {
- eventCountCondition.await();
- }
- } finally {
- eventCountLock.unlock();
- }
- // Print the elapsed time
- System.out.println("Elapsed time (ms): " + (System.currentTimeMillis() - startTime));
The final portion of this sample's code (shown above) connects to the DXL fabric and sends out 1000 events on the /isecg/sample/basicevent topic. The body of the for loop is iterated for TOTAL_EVENTS times (1000). The first step in the body of the for loop is to construct a new Event that will be sent to the /isecg/sample/basicevent topic. Next, a payload is set on the event that contains the index of the loop (from 0 to 999). The final line of the loop body sends the event to the DXL fabric by invoking the sendEvent method on the client.
Service-based Communication
The DXL fabric allows for “services” to be registered and exposed that respond to requests sent by invoking clients. This communication is point-to-point (one-to-one), meaning the communication is solely between an invoking client and the service that is being invoked. It is important to note that in this model the client actively invokes the service by sending it requests.
For example, the McAfee Threat Intelligence Exchange (TIE) service is exposed via DXL allowing for DXL clients to actively request reputations for files and certificates.
Run Sample
A "basic service sample" is included with the OpenDXL Java Client SDK that demonstrates registering and invoking a DXL service.
To run the sample execute the following from the root directory of the extracted OpenDXL Java Client SDK:
sample\runsample sample.basic.ServiceSample
Sample Output
The output should appear similar to the following (a request is received by the service and it delivers a response back to the invoking client).
Code Details
This section walks through the different portions of code that comprise the "basic service sample" in detail. Full code for the sample is available within the extracted SDK or via the OpenDXL Java Client GitHub Repository.
As shown above, a variable is defined that contains a topic to associate with the service. Multiple topics can be associated with a service, each topic represents a different "function" that can be invoked by remote DXL clients.
- // Create incoming request callback
- final RequestCallback myRequestCallback =
- request -> {
- try {
- System.out.println("Service received request payload: "
- + new String(request.getPayload(), Message.CHARSET_UTF8));
- final Response res = new Response(request);
- res.setPayload("pong".getBytes(Message.CHARSET_UTF8));
- client.sendResponse(res);
- } catch (Exception ex) {
- ex.printStackTrace();
- }
- };
- // Create service registration object
- final ServiceRegistrationInfo info = new ServiceRegistrationInfo(client, "myService");
- // Add a topic for the service to respond to
- info.addTopic(SERVICE_TOPIC, myRequestCallback);
- // Register the service with the fabric (wait up to 10 seconds for registration to complete)
- client.registerServiceSync(info, TIMEOUT);
The next portion of code (shown above) creates a DXL service registration that will respond to requests that are received on the /isecg/sample/mybasicservice topic.
The first step is to implement the RequestCallback interface which will be invoked for each request that is sent to the DXL fabric for the service topic that is associated with the callback.
For each Request message received, the payload is displayed. Next, it creates a Response message that will be sent back to the invoking client. The payload of the Response message is set to "pong". The last line of this method sends the response back to the invoking client by passing the newly created Response object to the sendResponse method of the client.
The final three lines in this portion create a service registration, associate the newly created request callback with a service topic, and register the service with the DXL fabric. A ServiceRegistrationInfo object is created which represents the service (named "myService") to register. The service topic /isecg/sample/basicservice is associated with the request callback by invoking the addTopic method of the service registration object. Finally, the service is registered with the DXL fabric by passing the service registration object to the registerServiceSync method of the client.
- // Create the request message
- final Request req = new Request(SERVICE_TOPIC);
- // Populate the request payload
- req.setPayload("ping".getBytes(Message.CHARSET_UTF8));
- // Send the request and wait for a response (synchronous)
- final Response res = client.syncRequest(req);
- // Extract information from the response (Check for errors)
- if (res.getMessageType() != Message.MESSAGE_TYPE_ERROR) {
- System.out.println("Client received response payload: "
- + new String(res.getPayload(), Message.CHARSET_UTF8));
- } else {
- System.out.println("Error: " + ((ErrorResponse) res).getErrorMessage());
- }
The next portion of code (shown above) invokes the DXL service that was registered in the previous code portion and displays the information contained in the received response.
The first step is to create a Request message that will be sent to the /isecg/sample/basicservice topic. The payload of the Request object is set to "ping".
Next, the service is invoked by passing the newly created Request object to the syncRequest method of the client. This method will wait until a Response is received from the service or a timeout occurs.
Finally, the message type is checked to ensure the Response received is not an ErrorResponse. If it is not an error, the payload associated with the Response message is displayed.