Last month Solace released version 1.0 of a new messaging API for Java. Why is it version 1.0 when we already have a Java API, you might ask? I’ll break that down first, then introduce the new one and explain why it’s such a big deal.
A Tale of Two Java APIs
“What’s in a name? That which we call a rose by any other name would smell as sweet.”
– Juliet
While I myself called this new API “a new Java Messaging API” in the title of this post, and its official name is “Solace PubSub+ Messaging API for Java”, in the public documentation and product information, it will henceforth be known as the “Java API”. For this post, I’ll err on the side of ensuring clarity by calling it the new Java API.
So in deference to Juliet above, going forward the old/existing Java API, which was named “Solace PubSub+ Messaging API for Java” is being renamed “PubSub+ Messaging API for Java (JCSMP)” and will generally be referred to as the JCSMP API, but sometimes you may see it called just “JCSMP” or the “classic” Java API. (JCSMP, if you’re interested, stands for “Java Solace content subscription management protocol.”) While it might take a minute to get used to the naming, I assure you this is not a case of what British rockers The Who famously worried about when they sang “the new boss is same as the old boss.”
Why a Second “New Approach” Java API was Needed
The JCSMP API is our oldest and most popular API, but as PubSub+ Platform evolves and is implemented by more customers across more use cases, we decided JCSMP needed a more modern API, and this new Java API is the result. The new Java API is implemented as an API layer that wraps functionality in JCSMP.
The JCSMP API will not be deprecated or removed from Solace’s supported products – all existing and future JCSMP applications will continue to work, and are fully supported by Solace.
In the JCSMP API there are many references to Solace’s legacy of XML content routing. One of the most confusing aspects of this is the XMLMessage
interface. This is how you handle messages in JCSMP and refers to the internal representation of the message, but to an application developer using binary attachments or text messages, this is not actually limited to XML payloads. Try explaining to a developer new to PubSub+ that BytesXMLMessages
are not XML messages (unless they want them to be, of course)!
With the new Java API, Solace introduces a more modern Java implementation. The new Java API requires Java 1.8+ as it uses some aspects of the Java language introduced in Java 8.
Let’s talk about some of the new features of the new Java API as it was certainly more than a find/replace of “XML” in the source code of the JCSMP API.
Dispatching Messages to Subscribed Receivers
“Lift up the receiver, I’ll make you a believer.”
– “Personal Jesus” by Depeche Mode
The PubSub+ C API has a feature called “Topic Dispatch” on consumers where 1-n dispatch callbacks can be registered on a session. The dispatch callbacks specify a topic filter where the filter determines if received messages are dispatched/sent to that callback.
This feature is unique to the C API (and its .Net derivative). With JCSMP, you add subscriptions to the session and the consumer on that session will receive all the messages matching those subscriptions. There is no option to have different handlers for different topics or sets of topics.
The new Java API implements a different model, consistent with the Python API. In this model, you create a connection to the broker and then add 1 or more MessageReceiver
to it. For each MessageReciever
you can add or remove subscriptions at any time and that MessageReceiver
will be dispatched messages matching the subscription(s). This design effectively implements topic dispatch where each MessageReceiver
receives only messages it wants.
Here is an example:
public class HowToUseMessageSubscriptions { /** * Example how to add topic subscriptions to direct message receiver at build time and/or later at * when it is already created * * @param service connected instance of a messaging service, ready to be used * @param initialSubscription topic subscription to be added at build time * @param anotherMessageSource topic subscription to be added after receiver is already created * @throws java.lang.InterruptedException when thread is interrupted */ public static void addAnotherSubscription(MessagingService service, TopicSubscription initialSubscription, TopicSubscription anotherMessageSource) throws InterruptedException { final DirectMessageReceiver receiver = service .createDirectMessageReceiverBuilder().withSubscriptions(initialSubscription).build() .start(); // any time later can subscribe to messages from a new message source receiver.addSubscription(anotherMessageSource); }
Reference: Java API Tutorials – How to use message subscriptions
Utilization of Builder Pattern
“Build it and they will come”
– 1989 film Field of Dreams
(although as a great example of a Mandela effect, the whispered line is actually “HE will come” referring to Shoeless Joe Jackson)
The new Java API heavily features the use of the Builder Pattern. There are builders available for most of the messaging API objects in the API such as:
MessagingServiceClientBuilder
to build aMessagingService
PersistentMessagePublisherBuilder
to build aPersistentMessagePublisher
OutboundMessageBuilder
to build anOutboundMessage
- And many more…
The methods to the builders are designed so that the building of these objects is fluent and readable. Someone unfamiliar with the Java API could look at the builder code and easily understand the intent of the original app developer.
Consider this example taken from a tutorial at https://tutorials.solace.dev/java:
public static MessagingService tlsWithCertificateValidationAndTruststoreSettings() { // enables acceptance of expired certificate final boolean ignoreExpiration = true; final TransportSecurityStrategy transportSecurity = TLS.create() .withCertificateValidation("myTruststorePassword", ignoreExpiration, SecureStoreFormat.JKS, "/my/trustStore/FilePath"); return MessagingService.builder(ConfigurationProfile.V1).local() .withTransportSecurityStrategy(transportSecurity) .build().connect(); }
In this example, we are using the MessagingServiceBuilder
to create a MessagingService
and using 4 methods to do it fluently.
local()
– is a helper method to allow you to quickly get up and running with an event broker running on localhost on port 55555 and on the default message VPN. This could be replaced byfromProperties()
for other deployments.withTransportSecurityStrategy()
– to enable TLS transport with the broker.build()
– creates aMessagingService
using the supplied configuration (and uses defaults for properties not supplied in the builder)connect()
– connects to theMessagingService
. Not a method of the builder, it’s a method of theMessagingService
As you can see, the builders make for an easy, fluent, readable way of creating the underlying objects and effectively separate the creation of an object from its representation in the API.
Simplifying Event Handling
“The Future’s So Bright, I Gotta Wear Shades”
– song by Timbuk 3
The JCSMP API has a single event-handler the application must code to handle all asynchronous events, including confirmations for asynchronous actions like connect/send-request/send-subscriptions. This means the async code for the JCSMP API is unique to this API and not standard to Java developers coming from other APIs.
The new Java API returns CompletableFuture
for many of its asynchronous operations. CompletableFuture
is a standard class for handling async behaviors and is well-known to Java developers.
From the excellent Baeldung Blog: “Java 8 introduced the CompletableFuture class. Along with the Future interface, it also implemented the CompletionStage interface. This interface defines the contract for an asynchronous computation step that we can combine with other steps. CompletableFuture is at the same time a building block and a framework, with about 50 different methods for composing, combining, and executing asynchronous computation steps and handling errors.”
Here is an example of the use of CompletableFuture
in the MessagingService
class:
/** * Example how to connect to a broker fully asynchronously using Async API. See {@link * CompletionStage} API for more use cases * * @param service configured instance of {@code MessagingService} ready to be connected * @see <a href="https://community.oracle.com/docs/DOC-995305">CompletableFuture for Asynchronous * Programming in Java 8</a> */ public static void asynchronousConnect(final MessagingService service) { // promiseOfConnectedService can be used to start publisher or receiver asynchronously // or other operations final CompletionStage<MessagingService> promiseOfConnectedService = service.<MessagingService>connectAsync() .whenComplete((messagingService, throwable) -> { if (throwable != null) { // This method can't prevent exception propagation. // Exception logging can be performed here, // or code can be placed here that releases some external resources. // IMPORTANT: when exception is occurred during connection creation, then // this exception is propagated over entire call chain, preventing ALL another calls // from execution (receiver won't attempt to start, message listener won't be registered) // IMPORTANT: 'throwable' is of type java.util.concurrent.CompletionException, to get out nested exception use: final Throwable wrappedException = throwable.getCause(); // wrappedException is very likely of type PubSubPlusClientException, use cast/type check for processing } }); }
Reference: Java API Tutorials – How to Access a PubSub+ Service
CompletableFuture
in the Java API makes asynchronous code easier to write and builds on existing skills of Java developers and is a welcome addition to PubSub+ client applications.
Better Handling and Retrieval of Message Payloads
Above I described how the JCSMP API handles XMLMessage
can be confusing for developers new to the API. In the new Java API, the designers made handling and retrieval of the message payload much easier. There are methods to directly get the text or bytes of the message payload without having to further cast the message into types such as BytesMessage
or TextMessage
.
For example:
final MessageHandler messageHandler = (message) -> { byte[] bytes = message.getPayloadAsBytes(); };
Or:
final MessageHandler messageHandler = (message) -> { String myPayload = message.getPayloadAsString(); };
These methods will do the proper conversions for you and return a byte array or UTF-8 encoded string respectively.
Conclusion
These are just a few of the features of the new Java API. There is a lot more to discover and a good place to start is the Java API Tutorials page where there are numerous functional examples of how to use the API to accomplish specific tasks. Many of them come with classes that show off the options of the API. It’s well worth a look.
This is just the beginning of the Java API. We have released version 1.0 and the roadmap contains many more features. The Java API is the “go to” for Java developers writing applications for PubSub+. We encourage developers to consider using the Java API for new applications targeting your PubSub+ Event Broker services. It is available now via Maven Central Repository, the customer product portal, and on the Downloads page at Solace.com.
For further discussions and questions about the Java API, don’t be shy to join us on the Solace Developer Community where you can find other community members and Solace employees discussing several topics and answering questions.
The future really is so bright that you may want to wear your shades!
Explore other posts from categories: For Architects | For Developers