A few days ago I sent a question to Arun Gupta on Twitter so I could start off my new years resolution - learn, promote and share.
@arungupta Pick a #javaee topic (6 or 7!) and I’ll investigate and blog about it. I’ll learn, promote and share! My idea for 2013! :-)
Arun replied with a list of topics but Bruno Borges narrowed it down to one - JMS 2.0. The JMS specification is up for renewal and the public review draft has been published on java.net. Note that this is a public review draft and we, the community, are invited to comment.
I’m not too familiar with JMS but in the spirit of learning and sharing I dived into the draft! This turned out to be quite fun and I ended up writing this fairly long post that touches on a few subject in the JMS world. I’m by no means an experts and do appreciate comments and errata!
JMS
JMS, as you may already know, provides a common way for Java programs to create, send, receive and read an enterprise messaging system’s messages. The review draft has a section that mentions that this is not at all like email. Enterprise messaging is about connecting systems. Messages, as described here, are asynchronous requests, reports or events that are consumed by enterprise applications, not humans.
A message consists of a known set of headers, a message body of some type and maybe some properties (also known as optional headers). This is all defined in the javax.jms.Message interface.
The asynchronous part is interesting. The message producer simply places the message in the hands of a service - a message broker - that ensures delivery to the recieving systems whenever they are available. This is a key feature that has been available since…well.. forever I assume.
A simple example could be illustrated like this:
- Student A enrolls in course B
- The course enrollment module sends a message to the message broker - Hey, student A has enrolled in couse B!
- The message broker delivers the message to the subscribers
- The subscribers (recipients..) react in some way
- Recipient 1 composes a welcome message for the student and emails it
- Recipient 2 adds access to the computer lab for student A
- Recipient 3 recipient logs the message to an audit log
- Recipient 4 system notifies the teacher and sends him/her an updated list of students
The cool part here is that the course enrollment module doesn’t need to know about the recipients. It simply tells the broker about the event and then goes on with it’s business as usual. This gives us a nice decoupling.
New features
Some new features have been added in JMS 2.0. The following three features are highlighted in the draft
- Delivery delay
- a message producer can now specify that a message must not be delivered until after a specified time interval. Kind of like a “do not open until xmas”-label on a package but with the difference that the message (or xmas package..) will be held by the broker (or post office..) until delivery time
- New send methods have been added to allow an application to send messages asynchronously.
- I’ve added a few notes on this further down in this entry
- JMS providers must now set the JMSXDeliveryCount message property.
- I haven’t dived into this yet
None of the new features seem like must have features to me. But I only glanced them over quickly. There are some other very nice changes though, so read on.
A cleaner API
The API seems to be very clean! The old API left us dealing with lots of boilerplate code. I won’t bother showing many examples since Arun Gupta has already published a nice example showing some of the improvements. But here’s a short teaser of how we could send a simple message about a student enrolling in a course..
1 2 3 4 5 | |
This would send a textmessage like xxxx:yyyy to the enrollmentTopic, which should be an implementation of javax.jms.Destination. This destination can be found through JNDI or some other method. Sending the message to the broker is a synchronous operation meaning that the method above will block and wait for acknowledgement from the JMS server. For those of you are in a real hurry or for some other reason prefer to do an asynchrounous send the new API provides methods for this too. Sending of the message will return immediatly and the actual sending of the message will be performed in a separate thread without blocking the caller. When the message is actually send, a callback object is method is invoked and only then can you be sure that the message has actually been sent. The API looks like this..
1
| |
Many of us are used to Spring and the JMSTemplate that hides the complexities of the old API. I can honestly say I’ve never used the standard JMS API but I have used JMSTemplate on a few occasions and other times I’ve used Apache Camel to interact with JMS endpoints. But by cleaning up the standard API we enable everyone to focus on better things. Even those who, for some reason, can’t or don’t want to add extra libraries to their applications benefit from this. Everyone benefits from a good standard API.
If you looked at Arun’s examples you probably noticed that the new API makes use of a cool new feature in Java 7 - the Autoclosable interface. Who isn’t sick of try-catch-finally blocks? Well, now we have try-with-resources and that will surely make our code a lot cleaner. But then again, those people who are used to the runtime exceptions from the Spring runtime exception philosophy probably don’t have a code base littered with try-catch-finally. But even those people can’t argue against a feature like Autoclosable and the Spring folks will also benefit greatly from this new feature. But I feel I’m drifting away from the topic here :-)
PTP and pub/sub
When dealing with JMS you are either dealing with a point to point or a publish/subscribe messaging model. The example provided earlier with the course enrollment is a publish/subscribe model where JMS clients can subscribe to messages from a well-known node in a content-based hierarchy. In the JMS world we call these nodes topics.
Topics usually have multiple subscribers and queues often only have one system consuming messages from the queue. The consuming system in itself might have several different nodes or competing consumers.
When dealing with subscribers you should also know about the term durable subscription. A non-durable subscription will only recieve messages when the subscriber is online. So if you take your subscribing system down for maintenance you will miss any messages produced during the service window. This might be tolerable but if it isn’t you should make sure that your subscription is durable. There are - or used to be - some issues with this and if you are using ActiveMQ you might want to look into virtual destinations instead. Other JMS servers might offer different solutions. I believe some of the shortcomings addressed by virtual destinations are fixed in the new specification. The following leads me to believe so:
In JMS 1.1, a durable or non-durable topic subscription was not permitted to have more than one consumer at a time. This meant that the work of processing messages on a subscription could not be shared amongst multiple threads, connections or JVMs, thereby limiting scalability. This restriction has therefore been removed in JMS 2.0.
Two key things to remeber regardless if you are dealing with queues or topics:
- Messages will be delivered once, and only once, to a subscriber (Exception handling might complicate this a bit..)
- Messages will always be delivered in the correct order
That’s actually a pretty nice contract. This, together with the asynchrounous nature of JMS, makes it possible for a publisher to publish a message and then forget about it. Often referenced as fire and forget. Transactions and replies to messages make this a bit more complicated but the fire and forget analogy will hold for many cases. The example provided earlier with the student enrollment module is such a case.
The JMS specification does not mention very much about the creation of queues and topics. That’s something that is left to the system administrator but in section 4.4.4 of the spec it’s mentioned that some specialized clients may need to create Destination objects by dynamically manufacturing one using a provider specific destination name. Sessions provide a JMS provider-specific method for doing this.
Personal conclusions
I really like the new API! The use of new features in the java language is a good thing. I can’t see that I will benefit much from any of the new features straight away but I’m sure some use cases will pop up.
I don’t think I can add any valuable comments to the spec draft at this moment but I will continue to investigate during the review period and I will add atleast one more blog entry on this topic. Hopefully with some code examples. That will give me a good excuse to download Glassfish 4 :-)