Skip to main content
March 15, 2015

Dynamic message passing with Java

Imagine this scenario, you've stepped outside of Spring MVC and are reading messages from a different source (either a websocket, MQ, or vanilla TCP socket). No longer can you rely on Spring to automatically route incoming messages to the correct handler method. This is what less battle tested developers will toss down into their IDE:

privatestaticfinal ObjectMapper MAPPER = new ObjectMapper();

publicvoidhandleMessage(byte[] message){
  // How do I tell what type of message this is?

And at that point you've got to wonder if there's an easier way. Not only do you want to figure out what type of message you've got without having to read bytes manually, but you also need to figure out how to send each message to the code to deal with specific message types. Let's start with the first issue.

Deserializing to the correct object

If you happen to be using Jackson for message serialization you're in luck, although other libraries will offer solutions as well. Suppose you have a well-defined class hierarchy for your messages as such:

publicabstractclassAbstractMessage {private String messageId;

  // Imagine getter and setter methods for messageId

Then assume each message we send over the wire is a descendent of that base class

public classHeartbeatMessageextendsAbstractMessage{
  // Yadda yadda yadda
public classShutdownMessageextendsAbstractMessage{
  // More yadda yadda yadda

With that sane message class structure, let's enable Jackson to automatically determine what object type to deserialize a field to based on extra metadata inserted into the serialized JSON text. It's actually insanely easy and can be done in one line:

@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="@class")

Add that to the top of each of your message classes, and each time Jackson serializes an object it will automatically embed a property named "@class" into the serialized text. During deserialization Jackson will read that property and automatically deserialize into the correct object type.

Now we can deserialize!

privatestaticfinal ObjectMapper MAPPER = new ObjectMapper();

publicvoidhandleMessage(byte[] message)throws IOException, JsonParseException, JsonMappingException
  AbstractMessage msg = MAPPER.readValue(message, AbstractMessage.class);

Dynamic Handling

Notice that even with the new annotation and metadata, the most specific we can deserialize to is an AbstractMessage. This is Java's static typing at play. If stopped here in a debugger you'd notice that the specific object type is in fact resolved at runtime. A naive routing handler would look like this:

if (msg instanceof HeartbeatMessage) {
} elseif (msg instanceof ShutdownMessage) {

This works ok with two message types, but what about 10, 20, or 50? Let's find some way to route messages without resorting to this. As it happens, Google's Guava library has an EventBus that will do the heavy lifting for us! I won't go into details about the EventBus here, but check out the Guava tutorials.

All that's needed is to add an EventBus to our handling class and annotate a few methods:

  privatestaticfinal ObjectMapper MAPPER = new ObjectMapper();

  privatefinal EventBus eventBus = new EventBus();


  publicvoidhandleMessage(byte[] message)throws IOException, JsonParseException, JsonMappingException
    AbstractMessage msg = MAPPER.readValue(message, AbstractMessage.class);;

  @SubscribepublicvoidhandleHeartBeat(HeartbeatMessage msg){
    // Handle the heartbeat specific message here

  @SubscribepublicvoidhandleShutdown(ShutdownMessage msg){
    // Handle the shutdown specific message here.

On line 13, we post the deserialized message to the EventBus. Even though we don't know the specific type the bus will resolve it and look for any handlers that specify the correct type. The annotations on line 16 and 21 tell EventBus which methods are available for injecting messages into.

Lastly, I threw in an init method, but you can register the handling object with the EventBus from anywhere.

With the above code, we've removed the need to manually resolve message types and made the routing happen behind the scenes. Enjoy not debugging and maintaining the code you didn't have to write ;)

Happy coding!


Love to learn about Application Security?

Get all the latest news, tips and articles delivered right to your inbox.