Integrating CDI and WebSockets

Thought of experimenting with a simple Java EE 7 prototype application involving JAX-RS (REST), WebSockets and CDI.

Note: Don’t want this to be a spoiler – but this post mainly talks about an issue which I faced while trying to use web sockets and REST using CDI as a ‘glue’ (in a Java EE app). The integration did not materialize, but a few lessons learnt nonetheless 🙂

The idea was to use a REST end point as a ‘feed’ for a web socket end point which would in turn ‘push’ data to all connected clients

  • JAX-RS end point which receives data (possibly in real time) from other sources as an input to the web socket end point
  • Use CDI Events as the glue b/w JAX-RS and WebSocket end points and ‘fire’ the payload


@Path("/feed")
public class RESTFeed {
@Inject
Event<String> event;
@POST
@Consumes(MediaType.TEXT_PLAIN)
public void push(String msg) {
event.fire(msg);
}
}

view raw

RESTFeed.java

hosted with ❤ by GitHub

  • Use a CDI Observer method in the WebSocket endpoint implementation to push data to connected clients


public void onMsg(@Observes String msg) {
//different WS enpoint instance – notice the hash code value in the server log
System.out.println("WS End point class ID — " + this.hashCode());
try {
client.getBasicRemote().sendText(msg);
} catch (IOException ex) {
Logger.getLogger(ServerEndpoint.class.getName()).log(Level.SEVERE, null, ex);
}
}

view raw

WSEndpoint.java

hosted with ❤ by GitHub

Of course, finer details like performance, async communication etc have not being considered at this point of time. More of an experiment

But is this even possible ?

Here are the steps which I executed

start

  • Fired a HTTP POST request on the REST end point using Postman

fire-rest

 

Boom! A NullPointerException in the Observer method – I waited for a few seconds and then reality hit me!

NPE

 

Root cause (from what I understand)

  • Behavior of WebSocket end points

WebSocket end points are similar to JAX-RS resource classes in the sense that there is one instance of a web socket endpoint class per connected client (at least by default). This is clearly mentioned in the WebSocket specification. As soon as a client (peer) connects, a unique instance is created and one can safely cache the web socket Session object (representation of the peer) as an instance variable. IMO, this a simple and clean programming model

WS-spec

  • But the CDI container had other plans !

As soon as the REST end point fires a CDI event (in response to a POST request), the CDI container creates a different instance of the WebSocket endpoint (the CDI Observer in this case). Why? Because CDI beans are contextual in nature. The application does not control the instances of CDI beans. It just uses them (via @Inject). Its up to the container to create and destroy bean instances and ensure that an appropriate instance is available to beans executing in the same context. How does the container figure out the context though ? It’s via Scopes – Application, Session, Request etc…..

(again, clearly mentioned in the CDI specification)

CDI-spec

So, the gist of the matter is that there is NO instance of the WebSocket endpoint current context – hence a new instance is created by CDI in order to deliver the message. This of course means that the instance variable would point to null and hence the NPE (Duh !)

So the question is . . .

Which CDI scope is to be used for a WebSocket end point ??? I tried @ApplicationScoped, @SessionScoped and @RequestScoped without much luck – still a new instance and a NPE

Any other options ??

  • Defining a Set of Session as static variable will do the trick


private static Set<Session> peers = Collections.synchronizedSet(new HashSet());

But that IMO is a just a hack and not feasible in case one needs to handle client specific state (which can only be handled as instance variables) in the observer method – it’s bound to remain uninitialized

  • Server Sent events ? But at the end of the day, SSE != WebSocket. In case the use case demands server side push ‘only’, one can opt for it. SSE is not a Java EE standard yet – Java EE 8 might make this possible

Solution ?

I am not an expert – but I guess it’s up to the WebSocket spec to provide more clarity on how to leverage it with CDI. Given that CDI is an indispensable part of the Java EE spec, it’s extremely important that it integrates seamlessly with other specifications – specially HTML5-centric specs such as JAX-RS, WebSocket etc

This post by Bruno Borges links to similar issues related to JMS, CDI and WebSocket and how they integrate with each other.

Did I miss something obvious? Do you have any inputs/solutions? Please feel free to chime in ! 🙂

The sample code is available on GitHub (in case you want to take a look). I tried this on GlassFish 4.1 and Wildfly 8.2.0

That’s all for now I guess…. 🙂

Also check out

… a new eBook – Java WebSocket API Handbook

Cheers!

About Abhishek

Loves Go, NoSQL DBs and messaging systems
This entry was posted in Java, Java EE and tagged , , , . Bookmark the permalink.

4 Responses to Integrating CDI and WebSockets

  1. Pingback: Integrating CDI and WebSockets - Java吧

  2. joakim says:

    I’m in the middle of implementing CDI/Weld support for Jetty’s JSR WebSocket at the moment. (due in Jetty 9.3.0 final release)

    Having an event from the JAX-RS thread propagate to the the threads of WebSocket endpoints doesn’t seem sane to me.

    Try this as an example: Setup 2 Servlets, one which is a semi-long lived Async style response. Initiate a request on the long lived servlet. While that is processing, make a request on servlet #2, which initiates a CDI event to long lived servlet #1, which adds something to its ongoing output. (this doesn’t work on CDI/weld btw.)

    A JSR WebSocket endpoint has an interesting, and not well defined by spec, lifecycle.

    A WebSocket connection is technically speaking, is no longer part of the HttpServletRequest after the connection has been upgraded to WebSocket. Its no longer HTTP, its WebSocket. In fact, most implementations destroy / recycle the HttpServletRequest and HttpServletResponse objects immediately after upgrade. Even access to seemingly obvious things like HttpSession can only really be done safely at the handshake and up to the point where the session itself times out. HttpSession timeout during long lived WebSocket endpoints is very common, and very confusing if you don’t understand it.

    Just to get a taste of this undefined nature of JSR WebSocket and CDI, here’s some open issues for you to read.

    https://issues.jboss.org/browse/CDI-370
    https://java.net/jira/browse/WEBSOCKET_SPEC-196
    https://java.net/jira/browse/WEBSOCKET_SPEC-229

    Like

    • Abhishek says:

      Thanks for reading the post joakim

      I will try your example later. My POV:

      1. “Having an event from the JAX-RS thread propagate to the the threads of WebSocket endpoints doesn’t seem sane to me.” – I am not sure why? The problem is not about JAX-RS,. It could have been a POJO – the problem is that CDI and WebSocket containers create different instances of the same class because there is no definite context in which the websocket endpoint class is executing – its not a contextual instance as required by CDI.
        CDI integrates with other specifications – EJB, JAX-RS, JSF etc I haven’t tried it, but I think that same would be successful if I replace the WebSocket endpoint with an EJB

      2. Regarding the JIRAs you pointed to – a couple of them have been mentioned in Bruno Borges’s blog [mentioned at the end of the post]

      3. thanks for sharing your knowledge w.r.t internals of how the Servlet specific components behave in context of a WebSocket connection

      4. WebSockt lifecycle is simple IMO (I do agree that you definitely know better since you are the one integrating it with CDI !) – but there are a few caveats. The most recent one which I came across was in this post by Websocket spec lead Pavel
        Cheers !

      Like

  3. Pingback: WebSocket and CDI integration.. again.. | Thinking in Java EE (at least trying to!)

Leave a comment