jsocketio – A Simple Java EE 7 Backend for socket.io

A while back, I blogged about how to use Websocket and Servlet support in Java EE 7 to implement a backend for socket.io library. After my experimentation, I concluded that the entire process (of writing the backend) can be wrapped in a framework.

After a few false start, I’ve finally got the first version out. The framework is called jsocketio (I’m using the time honoured way of prefixing a “j” to name the framework).

 

@SocketIO

Use the @SocketIO annotation to annotate your message handler. The annotation only takes one parameter which is the event name. The default event name is message if you omit the name.

The following handler

@SocketIO //Defaults to message event
public class CustomerHandler {
   ...

will be used to handle the following

var sock = io.connect("http://localhost:8080"); sock.send("hello jsocketio");

If you require a custom event name, then the following handler

@SocketIO("customer")
public class CustomerHandler {

will be used on the following code

sock.send("customer", "hello jsocketio");

Like JAX-RS, jsocketio also supports simple path tempates. However, jsocketio’s path templates is based on regular expression (see what Jamie Zawinski say about RE) and uses groups, instead of named parameters in JAX-RS, for isolating values in event names. For example the following

@SocketIO("customer/(([0-9]+))")
public class CustomerHandler {

will match this

sock.send("customer/1", "fred");
sock.emit("customer/3", "barney");
sock.json.emit("customer/5", {name: "wilma"});

To retrieve the matched value, eg. getting 1 from customer/1, you’ll have to get group 1. More on this later.

 

Message Handler Annotations

There are 2 types of annotation for regular message (@WhenMessage) and events (@WhenEvent).

@WhenMessage is used with the send() method. The message payload can be converted into any primitive type, JsonObject, JsonArray or to any Java object so long as the class have

  • a constructor with a single String parameter, or
  • a public static method call valueOf with a single String parameter eg Integer.valueOf(String)

@SocketIO
public class ToUppercase {
   @WhenMessage
   public void handle(String data, Session session) {
      session.getBasicRemote()
         .sendObject(Frame.send(data.toUpperCase()));
   }
}

First off, you will notice that message handler, the method with @WhenMessage, have 2 parameters: one of these must be the Websocket’s Session object unless your handler is not planning on sending response back to the client. The other parameter is the injected value from the packet. jsocketio will try to convert the payload to the target type according to the parameter type.

When you are sending reply back to the client, the replies must be in a socket.io frame. As you can see in the example above, jsocketio provide a utility method called Frame to construct frames. Once the frame has been created, use sendObject() method to send the frame.

Here are some more methods in Frame

public static SocketIOFrame send(JsonStructure json)
public SocketIOFrame void send(String message)
/* values are send as JSON array */
public SocketIOFrame void emit(String event, String... values)
/* Key/value pairs are converted to JSON object */
public SocketIOFrame void emit(String event, Map<String , String> map)
public static SocketIOFrame emit(String event, List<Map<String , String> items)
public static SocketIOFrame emit(String event, JsonArray array)
public static SocketIOFrame emit(String event, JsonObject object)

/* The following methods sends a control frames, meaning should be obvious */
public static SocketIOFrame connect()
public static SocketIOFrame disconnect()
public statsic SocketIOFrame heartbeat()

I’ve tried to make Frame intuitive by mirroring socket.io’s method; send() and emit()have the same semantics as its JavaScript counterpart.

Here is a slightly more complex example involving JPA and resource injection. I’m using the customer database from Derby. The entity classes, not shown, are generated using NetBeans.

@SocketIO("customer/(([0-9]+))
public class CustomerResource {
   @PersistenceContext EntityManager em;
   public void handle(@Group(1) int id, Session session){
      Customer customer = em.find(Customer.class, id);
      if (null == customer) {
         session.getBasicRemote().sendObject(Frame.send( 
            Json.createObjectBuilder().build());
         return;
      }
      JsonObjectBuilder builder = Json.createObjectBuilder(); 
      builder.add("name", customer.getName())
         .add("custId", customer.getCustomerId())
         ...;
      session.getBasicRemote().sendObject(Frame.send(
         builder.build()));
   }
}

 

Livecycle Events

To listen to jsocketio’s lifecycle event, use @WhenConnect and @WhenDisconnect. Since I’ve only implemented websocket support, so the parameters for the lifecycle events are the same as that of Websocket. @OnOpen and @OnClose.

/* Parameter are optional */
@WhenConnect
public void connect(Session session, EndpointConfig config) {
   ...
}

@WhenDisconnect
public void disconnect(Session session, CloseReason reason) {
   ...
}

 

Miscellaneous Items

One of the issue that I’ve raised in my previous blog on socket.io is the that socket.io browser clients need to set the resource parameter when connecting to a JavaEE backend. The reason is that all JavaEE web applications are deployed with an application context. So if you are connecting to a JavaEE backend with mysockapp as the context, then you must do the following to connect to it

var socket = io.connect("http://server:8080&quot;
   , { resource: "mysockapp/socket.io" });

jsocketio automatically adjust the resource parameter to match its context. So all you have to do from the client is

var socket = io.connect(“http://server:8080”);

One last item,  you have to set bean-discovery-mode=”all”  attribute in your beans.xml file. CDI 1.1 changes the default behaviour of how beans are looked up. See this blog. However Websocket handles does not support any of the CDI scopes apart from when the connection is made (@OnOpen). So to work around this issue, you have to change the discovery mode from annotated to explicit archive.

 

What Next?

I would like to add support for a fallback protocol. The obvious choice would be xhr-polling. Also I would like to abstract websocket specific objects from leaking in jsocketio’s handlers.

The source for jsocketio can be found here. The compiled JAR file can be found here.

Let me know what you think.

Till next time.

Advertisements

2 Responses to jsocketio – A Simple Java EE 7 Backend for socket.io

  1. Marcus says:

    Nice effort for writing a framework adapting Java EE and socket.io. Just a question, it works right now in 2017? Also There is repository like maven to get this lib?

    Tks!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s