如何让服务器向JSF创建的HTML页面推送异步更改?

24

当我们创建JSF页面时,客户端请求使用Java代码和HTML的组合动态生成HTML。在使用JSF框架创建HTML页面时,我们是否可以引入钩子,使得服务器能够根据稍后在服务器上发生的异步事件(通常通过不同的线程)更新HTML页面?

4个回答

37

JSF 2.3+

你可以使用@Push<f:websocket>来实现这个功能。下面是一个启动示例,其中包含一个应用范围的socket,它在后端通过Event#fire()触发的事件更新数据表,而这个事件由托管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-

如果您还没有使用JSF 2.3,您需要转向第三方JSF库。

需要注意的是,<o:socket>是JSF 2.3 <f:websocket>的基础。因此,如果您发现了很多相似之处,那就是正确的。

PrimeFaces在内部使用Atmosphere(没有Maven的设置比较麻烦)。 Atmosphere支持WebSocket,并回退到SSE和长轮询。ICEfaces基于古老的长轮询技术。所有这些都没有实现原生的JSR356 WebSocket API,该API只在Java EE 7中引入。

OmniFaces使用本机JSR356 WebSocket API(支持所有Java EE 7服务器和Tomcat 7.0.27+)。因此,设置和使用起来也是最简单的(一个JAR文件,一个上下文参数,一个标签和一个注释)。它仅需要CDI(在Tomcat上安装也不难install on Tomcat),但它使您甚至可以从非JSF构件进行推送(例如,@WebServlet)。出于安全性和JSF视图状态保持的原因,它仅支持单向推送(服务器到客户端),而不支持相反的方式。为此,您可以继续以通常的方式使用JSF ajax。JSF 2.3 <f:websocket>在很大程度上基于OmniFaces <o:socket>,因此它们的API中有很多相似之处(JSF - OmniFaces)。

或者,您还可以使用轮询而不是推送。几乎所有支持AJAX的JSF组件库都有一个<xxx:poll>组件,例如PrimeFaces中的<p:poll>。这允许您每隔X秒向服务器发送一个AJAX请求,并在必要时更新内容。与推送相比,效率稍低。

另请参阅:


1
在stackoverflow中,当我们编辑一个问题时,如果同时有其他人编辑该问题,我们会在编辑页面上收到一条消息。这是使用轮询还是服务器端推送实现的? - Rohit Banga
1
使用轮询。检查源代码并安装 [Firebug](http://getfirebug.com)以跟踪 XHR(Ajax)请求。 - BalusC
当页面以极快的速度发送许多更新时,不是所有的更新都被接收并显示在h:dataTable上。我该如何确保没有任何消息丢失? - seinecle
抱歉! - seinecle

0

-1

你可以看一下Seam(参见这篇文章,了解如何将Seam与JSF和AJAX一起使用)。

不过,我上次使用Seam时感觉它相当慢。你可能想要创建自己的JSF组件来生成JavaScript(例如,使用这篇文章中介绍的jQuery)。


-1
如果您需要完整功能的Comet更新(反向Ajax)等功能,则值得查看DWR库。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接