Saturday 29 September 2012

From backend event to Screen update with PrimeFaces Push

Introduction

Maybe the longest title of a post that I will ever write, but it gives a good idea about what the text explains.  There are various use cases where an action on the backend should be reflected on the screen of the users that are currently working with the application.
An example could be that the number of available places for a training is updated on the screen when another user finishes the registration for that same training. But not only the number on the screen should change, but when the counters reaches zero, we must disable the ‘submit’ button for the user who was almost ready with filling in his data.
With the recently released version 3.4 of PrimeFaces, the JSF community has an easy way of making those things possible. With PrimeFaces Push, powered by the Atmosphere framework, you can have parts of your JSF based screens updated by server side push technology.  And the best thing of all, you don’t need to write a single JavaScript statement to achieve this.  Those people that know me, know that this is a huge advantage because I can’t write a decent line of JavaScript.
The following code snippets assume you have an JEE6 application.

PrimeFaces Push

Lets start from the end result and work our way to the source that triggered the update on the screen. The setup of PrimeFaces Push is very easy.  I assume, you already use PrimeFaces in your project.  Since PrimeFaces Push is a layer around Atmosphere, we only need to add those libraries in our application. If you use Maven, it is sufficient to add the runtime to your maven POM file.
        <dependency>
            <groupId>org.atmosphere</groupId>
            <artifactId>atmosphere-runtime</artifactId>
            <version>1.0.0.RC1</version>
        </dependency>

You can always add the atmosphere libraries to the lib directory of your web app folder.

The second, and last, thing we need to do is to specify a servlet that is used as entry point for the web browser calls. The following snippet can be placed in the web.xml file.
    <servlet>
        <servlet-name>Push Servlet</servlet-name>
        <servlet-class>org.primefaces.push.PushServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Push Servlet</servlet-name>
        <url-pattern>/primepush/*</url-pattern>
    </servlet-mapping>

The URL pattern of the servlet mapping is of no importance.  We just need some value as entry point.

The screen


We now can adjust the JSF screen to integrate the push technology. The main PrimeFaces component for this is the p:socket component. It is responsible for all the stuff related to the server side push technology.  In the case you want to update a part of the screen in reaction to a notification from the server that an update is required, the following snippet is enough.
        <h:form id="form">
          ...
        </h:form>
        <p:socket channel="/registrationEvent">
            <p:ajax event="message" update="form:activities"/>
        </p:socket>


The JSF form contains the regular components that make up your page. The p:socket has an attribute that specifies the channel on which it needs to be bound.  You can define different channels so that you can send different types of events to the browser.  Here in this case, we say that we are interested in the event type registrationEvent. What should the screen do when it receives this kind of event from the server? Here we launch a partial page update with the p:ajax component and update a component. In the above example it has the id activities and it is for example a table containing all the trainings and the number of available places.
The attribute event of the ajax component, specifies that the partial update is triggered when we receive a message from the server.  But the actual payload of that message is never used and thus we will see in the next paragraph that we keep it very small.

Initiate the push


You can initiate the push to the browser on various locations in your code.  But I prefer to have it centralized according to the principles of DRY (Don’t Repeat Yourself) and Separation of concerns.  I have a certain class responsible for sending the events to the browsers and they will be triggered by CDI events as you can see in the following code sample.
public class NewRegistration {

    public void observeRegistrationActivity(@Observes RegistrationActivity someRegistrationActivity) {
        PushContext pushContext = PushContextFactory.getDefault().getPushContext();
        pushContext.push("/registrationEvent", "There was another registration");
    }
}


It is a CDI bean with a @Dependent scope (ApplicationScoped is also possible, see further on) and the method observeRegistrationActivity gets called by those code parts that do something with the registration for an activity like a training.  The CDI event gets fired over there (see next chapter) but doesn’t know what exactly is performed in response of that action (like an update of a browser screen).  Implementing the required functionality is the task of the various listeners of that CDI event.  This way we achieve a very loose coupling between the required functionalities.

The 2 lines of Java code comes straight from the PrimeFaces documentation and have the result that all registered browsers receive an event on the channel registrationEvent.  When we make sure that the channel name in the java code and in the p:socket component are the same, the result is that the partial screen update is performed. (tip: With custom EL function you can make sure that the channels is only defined once as a Java constant)

The scope of the CDI bean is important and must be @Dependent or @ApplicationScoped. Since request and session scoped beans are always linked with an HTTP request from the browser, those beans are not always ‘active’ which results in exceptions.

Initiate the event


We have now the possibility to trigger a partial screen update in the browser when we fire a certain CDI event. The code for firing such an event is easy and can be located in any kind of bean (CDI or EJB)
    @Inject
    private Event<RegistrationActivity> events;

    public void changedEntry(Activity someData) {
        events.fire(new RegistrationActivity(someData.getId()));
    }

When the method changedEntry is called, the whole chain is set into motion and the user sees the new information.

This code can be used in a Stateless Session Bean (EJB) when we have updated the database and have now a new value for the available places of the specified training.

But the placement of this kind of code can be much more exotic, let say an EntryListener of Hazelcast.
Hazelcast is a in-memory data grid solution that can be clustered and can be categorized as NoSQL.
For a distributed Map for example, you can register an EntryListener. Every time, any Hazelcast client makes a change to that map, the listener is triggered.  But such an EntryListener can be a CDI bean that fires the CDI event that goes all to way up to the browser.

This creates a very powerful system that any application that changes some value in a distributed Map of Hazelcast, will trigger an update on the screen.  Wooow.

Conclusion


PrimeFaces Push is an easy to setup system that uses the Atmosphere framework to integrate the server side push technology in a JSF component library.  Combined with the power of CDI events, you can trigger a screen update from anywhere in the application.  You can extend this even to a in-memory data grid solution.

And all this can be achieved by a few lines of code so that you once again can concentrate on the business problems and don’t have to worry about the infrastructure.