All it needs is a little ‘push’

A common requirement of a modern ‘reactive’ application is to refresh a web page when an event has occurred. Not necessarily one instigated by a user clicking on ‘something’, one independent of their actions. In effect ‘push’ a notification back to the web page, be this from an EJB or POJO.

In Java Server Faces (JSF), of which JSF 2.3 is the standard MVC web framework in EE8, this is very easily done using the ‘socket’ tag from the OmniFaces JSF library.

To trigger a push from the EAR/EJB side to an application-scoped push socket, you can make use of CDI events. First, create a custom bean class representing the push event something like the PushEvent class below taking whatever you’d like to pass as the push message.

 public final class PushEvent {

    private final String message;

    public PushEvent(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}

In my EJB, a Stateless Session Bean I use:

beanManager.fireEvent(new PushEvent(foodsCosmeticsMedicines.getBrand()));

to fire the CDI event.

public void onFoodsCosmeticsMedicinesChange(FoodsCosmeticsMedicines foodsCosmeticsMedicines) {
   beanManager.fireEvent(new PushEvent(foodsCosmeticsMedicines.getBrand()));
}

Finally you just ‘@Observes’ it in some request or application scoped CDI managed bean in the WAR and delegate to a PushContext as below.

@Named
@ApplicationScoped
public class PushBean {

    /**
     *
     */
    private static final String EVENT = "onPush";

    /**
     *
     */
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     *
     */
    @Inject
    @Push(channel = "databaseChannel")
    private PushContext databaseChannel;

    /**
     *
     */
    public PushBean() {
    }

    /**
     * @param pushEvent PushEvent
     */
    public void onPush(@Observes PushEvent pushEvent) {
        logger.info("***** onPush pushEvent getMessage = {}", pushEvent.getMessage());
        databaseChannel.send(EVENT);
    }
}

The PushBean class must be deployed in the WAR file.

In my Facelet (XHTML) I use OmniFaces Socket tag to pick up on the event.

<h:form>
    <o:socket channel="databaseChannel" onmessage="socketListener" >
         <f:ajax event="onPush" render=":tabHomeView:formFoodsCosmeticsMedicines:panelGridFoodsCosmeticsMedicines"/>
    </o:socket>
</h:form>

The onMessage executes some JavaScript, which is contained in:

<head>
    <script src="socketListener.js"></script>
</head>

The socketListener.js is:

function socketListener(message, channel, event) {
    console.log(message);
    console.log(channel);
    console.log(event);
}

When I run my JUnit test that persists a new Document into my MongoDB database the JSF panel grid in my XHTML gets re-rendered displaying the new ‘row’ that has just been inserted. The following appears in the Google Chrome logs:

onPush
11:38:30.550 socketListener.js:3 databaseChannel
11:38:30.550 socketListener.js:4 MessageEvent {isTrusted: true, data: ""onPush"", origin: "ws://localhost:8080", lastEventId: "", source: null, …}
11:38:30.659 jsf.js.xhtml?ln=javax.faces&stage=Development:2001 XHR finished loading: POST "http://localhost:8080/NOTiFYwell/index.xhtml".
AjaxEngine.req.sendRequest @ jsf.js.xhtml?ln=javax.faces&stage=Development:2001
sendRequest @ jsf.js.xhtml?ln=javax.faces&stage=Development:2700
request @ jsf.js.xhtml?ln=javax.faces&stage=Development:2710
ab @ jsf.js.xhtml?ln=javax.faces&stage=Development:3787
OmniFaces.Push.init.onPush @ (index):96
b.readyState.b.onmessage @ omnifaces.js.xhtml?ln=omnifaces&v=3.1:11

And in my JBoss WildFly 12 console I see:

11:38:30,533 INFO  [com.notifywell.ejb.FoodsCosmeticsMedicinesEJB] (default task-4) >>>>> onFoodsCosmeticsMedicinesChange getBrand = BONNE MAMAN

11:38:30,533 INFO  [com.notifywell.ejb.FoodsCosmeticsMedicinesEJB] (default task-4) >>>>> onFoodsCosmeticsMedicinesChange PushEvent getMessage = BONNE MAMAN

11:38:30,537 INFO  [com.notifywell.push.PushBean] (default task-4) ***** onPush pushEvent getMessage = BONNE MAMAN

The above code was developed using OmniFaces library 3.1 available to download from OmniFaces To make JSF life easier.

See the documentation at OmniFaces Showcase – push Socket.

 

Leave a Reply

Your e-mail address will not be published. Required fields are marked *