PrimeFaces Push 2.0

PrimeFaces Push has been around since 3.4 and with 5.0 it has been greatly enhanced thanks to the official partnership between PrimeTek (PrimeFaces) and Async-IO (Atmosphere).

First a few words on Atmosphere; The Atmosphere Framework is the most popular asynchronous application development framework for enterprise Java. The Atmosphere Framework provides the enterprise features required to build massive scalable and real time asynchronous applications using transports like WebSocket, Server Side Events and traditional Ajax Techniques.

  • It’s been proven in production: ReverbWall Street JournalGameDuellVMWareAtlassian and many more
  • Easy to write portable and asynchronous applications with a really simple API for both client and server
  • Supports WebSockets and fallback transports transparently
  • Proven to scale
  • Cloud Enabled!

PrimeFaces Push 2.0 is based on Atmosphere as its predecessor and follows an annotation based approach this time. Old APIs are deprecated and still supported for backward compatibility.

The easiest way to create PrimeFaces Push application is by using the @PushEndPoint annotation. This annotation makes it easy to build application with PrimeFaces Push without the need to interact with Atmospheres more sophisticated API. If you are familiar with the PrimeFaces Push 1.0, this new implementation significantly reduce the number of code required to build a powerful real time application by transparently installing Atmospheres components like heartbeat, idle connections detections, disconnect state recovery etc. It also allows the use of external dependency injection framework like CDI, Spring or Guice.

For example, PrimePush ships with an extremely simple chat application, which support multi room, all transports supported transparently, message caching and more. All of this in less than 100 lines! It is strongly recommended to look at the demos after reading this introduction to see how simple it is to build an application with Atmosphere.

@PushEndpoint
The annotations attributes available are

“path”: The path to the resource.

The default is “/” so if your have mapped the PushServlet to “/*”, all request will be delivered to your annotated class. You can also customize the path. The path value will be used to map an incoming request uris path to an annotated PushEndpoint class.

Class Scope Annotations;

@Singleton
Singleton annotation can be used to force PrimeFaces Push to create a single, thread safe instance of a PushEndpoint annotated classes. For example, if your application set the @ PushEndpoint’s path attribute with using path templates “{something}”, by default a new instance of the annotated classes will be created. When annotated with Singleton, a single class will be created.

Method Scope Annotations
When using the @PushEndpoint annotation, it is recommended to use the following annotations.

@OnOpen
The OnOpen() will be invoked when the underlying connection is ready to be used, e.g for write operations.

Annotated method needs to take the form of

@OnOpen
public void onOpen();

@OnOpen
public void onOpen(RemoteEndpoint r);

@OnOpen
public void onOpen(RemoteEndpoint r, EventBus e);

The RemoteEndpoint represents the physical connection and can be used to write some data back to the browser. The EventBus can be used to fire messages to one or more RemoteEndpoint using regex expressions.

@OnMessage

The OnMessage() will be invoked when a message is ready to be deliver, e.g as a result of a EventBus publish operation or when a browser is POSTing some bytes. The annotation’s attributes available are:
“encoders”: A list of [Encoder]() that will be used to encode the annotated method return value. The returned value of an annotated OnMessage method will be broadcasted to all resource associated with the Broadcaster, associated with this annotated classes. For example, the following will transform an object of type `Response` into a “String” ready to be sent back to the Browser.

public final class JSONEncoder implements Encoder<Response, String> {
    ObjectMapper mapper = new ObjectMapper();

    public String encode(Response s) {
        return mapper.writeAsString(s);
     }
}

Encoders can be chained, e.g the returned value of an Encoder can be used as an input for the next defined Encoder.

“decoders”: A list of [Decoder]() used to decode a broadcasted messages into an object matching the methods signature. For example, a message coming from a Browser can be transformed into an object before it gets delivered to the annotated @OnMessage method:

public class JSONDecoder implements Decoder&lt;String, Message&gt; {

@Override
public Message decode(String s) {
    String[] userAndMessage = s.split(":");
    if (userAndMessage.length <= 2) {
        return new Message().setUser(userAndMessage[0]).setMessage(userAndMessage[1]);
    } else {
        return new Message(s);
    }
  }
}

Decoders can be chained, e.g the returned value of an Decoder can be used as an input for the next defined Decoder. You can annotate several methods with the Message annotation.

@OnClose
The @OnClose() will be invoked when the client disconnect, e.g close the connection, when a network outage happens or when a proxy close the connection. Annotated method needs to take the form of:

@OnClose
public void onClose();

@OnClose
public void onClose(RemoteEndpoint r);

@OnClose
public void onClose(RemoteEndpoint r, EventBus e);

You can only annotate a single method with this annotation.

RemoteEndpoint

The RemoteEndpoint() class represents the remote connection, e.g the Browser. An instance of RemoteEndpoint hold information about the headers, queryString, body, uri, path and path segments that can be used for manipulating the incoming request. If you are familiar with the Servlets “HttpServletRequest”, the RemoteEndpoint can be seen as an improved version. You can also use a RemoteEndpoint#write to write back messages that will be delivered uniquely to the browser.

EventBus
A distributed lightweight event bus which can encompass multiple PushEndpoint instances. The event bus implements publish / subscribe and point to point messaging.
Messages sent over the event bus can be of any type. For publish / subscribe, messages can be published to a Java class annotated with the [PushEndpoint#value]()
using one of the method annotated with [OnMessage](). The EventBus is the recommended way for delivering messages.

Chat Sample

Demo is available at Labs Showcase.

First we annotate the class with the “PushEndpoint” annotation, and define the path to “{room}/{user}”. That means this class can be invoked with URI taking the form of “/room_1/userA”, “/room_2/userB”, “/room_1/userC”. In this example, userA and userC will be part of the same room, being able to chat together, where userB will be in another room. That’s all we have to define to support a multi room application.
We want our class to be stateless, so we use the @Singleton so we are guarantee to have a single instance of this class

The OnOpen annotated method (line 12) will use the EventBus for publishing a message to all connected users. Since we only want users of the same room to
received the message, we retrieve the rooms name by looking up the proper path segment (line 16). Same for the user, we use the path segment to retrieve the value send
by the browser (line 17).

Then we create a simple Message bean. That bean will be delivered to all users of our room. The bean will be delivered via the annotated OnMessage
method. In this particular case, the defined decoder won’t be called as the delivery object is already an instance of Message. We could have called EventBus.publish with a JSON String instead of a Message and the same method (line 27) would have been called. For example, when the browser sent a request, the requests body will be decoded using the JSONDecoder and so the requests body delivered as a Message bean.

Message are delivered to the OnMessage annotated method (line 27). There could be one or more method annotated with the OnMessage annotation. Encoder and Decoder are used by PrimePush to match the appropriate method. For this simple application, we don’t need to manipulate the Message by adding, removing of transforming its content. So we just sent back the Message using our Response bean. That bean will be encoded by the JSONEncoder, e.g its the Response object translated into a JSON String representation, and sent back to the Browser.

Finally, the method annotated with the OnClose (line 21) will be called when the browser close the connection, or when the connection gets broken for any reason. The OnClose method can be used to clean up resources.

Et voila! That’s the only code we need to write the server side of our PrimePush application.

More Demos

  • Counter: Global Click Counter
  • Notify: Push FacesMessages to all connected clients instantly.
  • ViewParam: Push data with a GET request using JSF APIs.
  • Checkin: Created with PrimeFaces Mobile, Push your location to everyone to be displayed on a map.
  • Chart: Keep track of visitor browser stats and push stats data to a chart component real-time.

This entry was posted in Uncategorized. Bookmark the permalink.

3 thoughts on “PrimeFaces Push 2.0

  1. I have a chat application that is based on the p:poll component. I think it would be major improvement to refactor it to use p:push.

    But the main issue is that this chat is made between two different contexts. Since the p:socket automatically appends the current context, is there a way to pass the full URL?

    Thanks

  2. Wow, now this is really really awesome, and this is such a good enhancement to PrimeFaces Push! Very nice blog post, thanks!!!