How can server push asynchronous changes to a HTML page created by JSF?
Asked Answered
L

4

25

When we create a JSF page, a client request allows generation of HTML dynamically using a combination of Java code and HTML. Can we introduce hooks in the HTML page using JSF framework, that allow server to update the HTML page based on asynchronous events occurring later at the server, usually via different threads?

Livingstone answered 24/9, 2010 at 13:23 Comment(0)
C
39

JSF 2.3+

You can use @Push and <f:websocket> for this. Below is a kickoff example with an applicaiton scoped socket which updates a data table upon an event fired by the backend via Event#fire() which the managed bean @Observes.

<h:dataTable id="notifications" value="#{bean.notifications}" var="notification">
    <h:column>#{notification.message}</h:column>
</h:dataTable>

<h:form>
    <f:websocket channel="push">
        <f:ajax event="updateNotifications" render=":notifications" />
    </f:websocket>
</h:form>
@Named @ApplicationScoped
public class Bean {

    private List<Notification> notifications;

    @Inject
    private NotificationService service;

    @Inject @Push
    private PushContext push;

    @PostConstruct
    public void load() {
        notifications = service.list();
    }

    public void onNewNotification(@Observes Notification newNotification) {
        notifications.add(0, newNotification);
        push.send("updateNotifications");
    }

    public List<Notification> getNotifications() {
        return notifications;
    }

}
@Stateless
public class NotificationService {

    @Inject
    private EntityManager entityManager;

    @Inject
    private BeanManager beanManager;

    public void create(String message) {
        Notification newNotification = new Notification();
        newNotification.setMessage(message);
        entityManager.persist(newNotification);
        beanManager.getEvent().fire(newNotification);
    }

    public List<Notification> list() {
        return entityManager
            .createNamedQuery("Notification.list", Notification.class)
            .getResultList();
    }

}

JSF 2.2-

If you're not on JSF 2.3 yet, you need to head to 3rd party JSF libraries.

Noted should be that the <o:socket> was the basis for the JSF 2.3 <f:websocket>. So if you have found a lot of similarities, then that's correct.

PrimeFaces uses Atmosphere under the hoods (which is troublesome to setup without Maven). Atmosphere supports websockets with fallback to SSE and long polling. ICEfaces is based on ancient long polling technique. All of those do not implement native JSR356 WebSocket API which was only later introduced in Java EE 7.

OmniFaces uses native JSR356 WebSocket API (supported in all Java EE 7 servers and Tomcat 7.0.27+). It is therefore also most simple to setup and use (one JAR, one context param, one tag and one annotation). It only requires CDI (not hard to install on Tomcat), but it enables you to even push from a non-JSF artifact on (e.g. a @WebServlet). For security and JSF view state keeping reasons, it only supports one-way push (server to client), not the other way round. For that you can keep using JSF ajax the usual way. The JSF 2.3 <f:websocket> is largely based on OmniFaces <o:socket>, hence you'll find a lot of similarities in their APIs (JSF - OmniFaces).

Alternatively, you can also use polling instead of pushing. Pretty much every ajax aware JSF component library has a <xxx:poll> component, such as PrimeFaces with <p:poll>. This allows you to send every X seconds an ajax request to the server and update the content whenever necessary. It's only less efficient than push.

See also:

Coyote answered 24/9, 2010 at 13:50 Comment(11)
In stackoverflow, when we are editing a question and in the meanwhile if someone else edits the question, we get a message on our editing page. Is that implemented using polling or server side push? – Livingstone
Using polling. Check the source and install Firebug to track XHR (Ajax) requests. – Coyote
when many updates are sent to the page at a great speed, not all of them get received and appear on the h:dataTable. How can I make sure that no message is lost? – Weightlessness
My apologies ! πŸ‘ – Weightlessness
I get this error on Tomee 8 (myfaces 2.3.10): Missing implementation of resolved method 'abstract java.lang.String getWebsocketURL(javax.faces.context.FacesContext, java.lang.String)' of abstract class javax.faces.application.ViewHandler. What to do? – Nissen
@GregTom: Remove conflicting JEE/JSF libraries from WEB-INF/lib. You don't need to include libs in WAR when server already provides them out the box. – Coyote
@BalusC: I have only primefaces-5.3.jar, all-themes-1.0.10.jar, gbit-persistence-1.0.jar (this is a small own library for JPA), and omnifaces-2.0.jar in WEB-INF/lib of WAR. None of these are in Tomee's lib folder. – Nissen
@GregTom: There's probably something else which provides its own ViewHandler since JSF 2.0 which incorrectly doesn't extend from ViewHandlerWrapper and therefore misses out new JSF 2.3 features. Perhaps PrimeFaces? (5.3 is super old!) Or OpenWebBeans? Put a debug breakpoint to know for sure and then upgrade or report issue to them. – Coyote
Maybe my old prettyfaces library makes the conflict.... – Nissen
@BalusC: Probably my old prettyfaces library makes the conflict.... I use prettyfaces-jfs2-3.3.3. [Here] (#58722340) I found that PrettyFaces 3.4.3 supports JSF 2.3. My problem is that this is prettyfaces-rewrite and my good old prettyfaces-3.3.3 seems to be a different thing. Can I use my good old pretty-config.xml with prettyfaces-rewrite? – Nissen
@BalusC: Yes, this was the problem. I've changed prettyfaces to 3.5.2.Final and it is OK! Thank you, your tips drove me to find the reason! – Nissen
A
0

Simplest for you can be introduction of ajax4jsf library's "poll" component: https://ajax4jsf.dev.java.net/nonav/documentation/ajax-documentation/entire.html#d0e1955

It will not need application reconfiguration and big changes in JSF page (only adding a4j:poll component)

It worked very good in couple of my projects.

Aristophanes answered 28/9, 2010 at 20:57 Comment(0)
B
-1

You can have a look at Seam (see this article for a discussion to use Seam with JSF and AJAX).

When I used Seam the last time, it was pretty slow, though. You may want to create your own JSF component that generates JavaScript (for example using jQuery as explained in this article).

Beanpole answered 24/9, 2010 at 13:28 Comment(0)
D
-1

If you need fully-featured Comet updates (reverse Ajax) and so on, then its worth taking a look at the DWR library.

Deuteranope answered 9/10, 2010 at 17:41 Comment(0)

© 2022 - 2024 β€” McMap. All rights reserved.