wesleyhales.com

Pushing CDI Events to the Browser with WebSockets

20 January 2012

html5 | websocket | jboss | jetty | cdi |

Disclaimer: Minimal load testing was performed with 10000 concurrent WebSocket connections. You can see some true performance numbers here.
Github Icon Download

Here is the demo in action. As you can see on the right, I have 2 chat windows open and on the left we have a member registration. Users are chatting across a raw WebSocket connection and when another user registers, the CDI event is fired all the way through to the browser as a JavaScript alert via the connected WebSocket clients.

With WebSockets, we have a new development model for server side applications; event based programming. There are 3 out-of-box events associated with WebSockets: onopen, onmessage, and onclose. For starters, we must wire up these three listeners to utilize the core functionality that the WebSocket specification gives us. The open event is fired when the WebSocket connection is opened successfully. The message event is fired when the server sends data. The close event is fired when the WebSocket connection is closed.

But sending messages in the form of strings over raw WebSockets isn't very appealing when we're wanting to develop advanced web applications. Obviously, we're going to be using JSON to transfer data to and from the server. But how do we propagate our CDI events which are fired on the server and have them bubble up on the client?

First, we'll start with the server. I'm using the JBoss AS7 application server and embedding Jetty within my web application. Thanks to this article, I was able to easily add the latest Jetty server to my maven project (dependencies below) to get everything up and running in a few minutes.

A few things worth noting:

  • Security: Since our WebSocket server is running on a different port (8081) than our AS7 server (8080), we must account for not having the ability to share cookies, etc...
  • Proxies: As if proxy servers weren't already a huge problem for running WebSockets and HTTP over the same port, we are now running the separately (but I have a semi-solution for this below)
  • Threading: Since we're observing/listening for CDI events, we must perform some thread same operations and connection sharing.

So, if you're still reading ;) let's get on with the code.

Download the latest JBoss AS7 (7.1.0.CR1b as of this writing)

Add the Jetty maven dependencies to your project. This demo is based off of the original html5-mobile quickstart for JBoss AS7.

Next we setup the WebSocket server using Jetty's WebSocketHandler and embedding it inside a ServletContextListener. Here we're sharing a synchronized set of WebSocket connections across threads. Using the synchronized keyword, we ensure that only a single thread can execute a method or block at one time. The ChatWebSocketHandler contains a global Set of webSocket connections and adds each new connection as it's made within the Jetty server View complete source here.

Now we'll create a method to observe CDI events and send the fired "Member" events to all active connections.

The above code will observe the following event when a new Member is registered through the web interface.

Finally, we setup our WebSocket JavaScript client and safely avoid using the eval() method to execute the received JavaScript.

Here is the JavaScript code which listens for our CDI event, and executes the necessary client side code. (This is the alert popup seen in the video above.)

One additional piece I added to this approach is the use of HAProxy. This gives us a reverse-proxy on the WebSocket port (8081), in the end allowing all traffic (HTTP and ws/wss) to be sent across a central port - 8080 in this case.

As you can see, this is a very prototyped approach to achieve $SUBJECT, but it's a step forward in adding a usable programming layer on top of the WebSocket protocol. There's probably a few framework out there which try to provide a programming model on top of WebSockets, so leave comments if you know of any.

Runtime Type Detection and Usage with Weld

04 May 2011

Java | cdi | hornetq | infinispan | jms | jsf | richfaces | seam | weld |

About TweetStream


tweetstreamIn developing the TweetStream demo for the JBoss World keynote and JUDCon presentation, I wanted to use CDI in a way that would choose the implementation of a given type at runtime. With Qualifiers and Producers, CDI gives you the power to do this.
A little bit about the usecase: The TweetStream application is an app that Jay Balunas and I developed over the past few months for our presentation at JUDCon and JBoss World 2011. It was purposely developed with a myriad of JBoss community projects to showcase how you can build a mobile HTML5 web application (which runs on Android and iOS devices) with things like scalable data grid, JMS, JSF2, HTML5/CSS3 and other middleware technologies. This application (TweetStream) was also chosen to be part of the literally incredible JBoss World 2011 keynote.
So, we had 2 scenarios – 1) for our presentation we needed a mobile app that could run solely on it’s own so that users could pull the source code, see how we did things, and run it. 2) For the keynote, we had to make our app integrate with the Infinispan datagrid that was already setup as part of the keynote demo. The data stored on this grid utilized Drools and complex event processing as part of the keynote, so our app had to consume that data for that environment.
So we got our tweet data from the true source (twitter4j) during our JUDCon presentation, and then from the data grid during the keynote. We could have used CDI alternatives, but I wanted a true solution with no XML configuration and runtime detection.


The Code...


So we have 2 Qualifier Types:
@TwitterLocal for the JUDCon demo impl
@TwitterServer for the keynote impl

We used infinispan in both instances, but our @TwitterLocal is a single node caching a direct twitter stream from Twitter4J.

Now that we have our types defined as follows…

@Qualifier

@Retention(RetentionPolicy.RUNTIME)

@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})

public @interface TwitterServer

{

}



@Qualifier

@Retention(RetentionPolicy.RUNTIME)

@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})

public @interface TwitterLocal

{

}

We need not only an implementation of each, but also a deciding bean that tells us which type to use.

First, our implementation of each Type implements an interface:

public interface TwitterSource {

  public void init();

And our implementations have a different usage of the init method. TwitterLocal starts the stream coming from twitter and updates the infinispan cache. TwitterServer starts a method which allows us to start receiving data from the keynote which uses complex event processing and a datagrid with 6–8 nodes.

So now, how do we decide which Type to use? There are a few different ways to do it, but in the case of this being a demo and not a lot of time on my part. I used this approach:

public class TweetStream {



  @Inject

  @Any

  Instance<TwitterSource> twitterSource;



  class TwitterLocalQualifier extends AnnotationLiteral<TwitterLocal> implements TwitterLocal

  {

  }



  class TwitterServerQualifier extends AnnotationLiteral<TwitterServer> implements TwitterServer

  {

  }



  boolean initialCheck = true;

  boolean demoexists = false;



  @PostConstruct

  private void init()

  {

     getTwitterSource().init();

  }





  @Produces

  public TwitterSource getTwitterSource()

  {

     if (initialCheck)

     {

        try

        {

                   Class.forName("org.jboss.jbw2011.keynote.demo.model.TweetAggregate");

           log.info("Running in JBW2011 Demo Mode.");

           demoexists = true;

        }

        catch (ClassNotFoundException ex)

        {

           log.info("Running in local JUDCon2011 Demo Mode.");

        }

        initialCheck = false;

     }



     Annotation qualifier = demoexists ?

        new TwitterServerQualifier() : new TwitterLocalQualifier();

     return twitterSource.select(qualifier).get();

  }

This is all in the source code. Feel free to pull it and make improvements or run it to see it in action. There are many more blog posts to come from this demo, so stay tuned…