Markus Dahm 在此处的博客条目中描述的解决方案
他将事件发送到 Web 应用程序的所有活动会话的解决方案包括扩展现有的 CDI 观察者模式以支持全局事件。
以下是相关代码:
首先,我们需要一个 GlobalevHttpSessionController 来注册和处理所有 HTTP 会话并将所有事件委托给会话:
@ApplicationScoped
public class GlobalevHttpSessionController {
public static final String
EVENT_ATTRIBUTE_NAME = "HttpSessionControllerEvent";
private final List _httpSessions = new ArrayList();
public List getHttpSessions() {
return new ArrayList(_httpSessions);
}
public void addSession(final HttpSession httpSession) {
_httpSessions.add(httpSession);
}
public void removeSession(final HttpSession httpSession) {
_httpSessions.remove(httpSession);
}
public void fireEvent(final GlobalEvent eventObject) {
for (final HttpSession session : _httpSessions) {
fireEvent(session, eventObject);
}
}
private void fireEvent(final HttpSession session, final GlobalEvent eventObject) {
try {
final List globalEvents = getGlobalEvents(session);
globalEvents.add(eventObject);
} catch (final Exception e) {
throw new IllegalStateException("fireEvent", e);
}
}
private synchronized List getGlobalEvents(final HttpSession session) {
List globalEvents = (List) session.getAttribute(EVENT_ATTRIBUTE_NAME);
if (globalEvents == null) {
globalEvents = new ArrayList();
session.setAttribute(EVENT_ATTRIBUTE_NAME, globalEvents);
}
return globalEvents;
}
}
其中 GlobalEvent 只是所有全局事件的简单可序列化超类:
public abstract class GlobalEvent implements Serializable {
private static final long serialVersionUID = 1L;
}
接下来,我们需要一个 HTTP 侦听器来添加和删除活动的客户端会话:
@WebListener
public class GlobalevHttpSessionListener implements HttpSessionListener {
@Inject
private GlobalevHttpSessionController _httpSessionController;
public void sessionCreated(final HttpSessionEvent se) {
_httpSessionController.addSession(se.getSession());
}
public void sessionDestroyed(final HttpSessionEvent se) {
_httpSessionController.removeSession(se.getSession());
}
}
为了将事件分派给客户端,他选择使用 JSF PhaseListener 查找传入事件。其他解决方案,例如使用过滤器,也是可能的。侦听器查找全局事件并使用 CDI bean 管理器将它们分派到本地会话。管理器实例是通过 JNDI 查找获得的(根据规范,bean 管理器必须由 java:comp/BeanManager 下的容器绑定)。不幸的是,我们不能在这里使用 CDI 注入,因为阶段监听器不是由 CDI 实例化,而是由 Java Server Faces (JSF) 框架实例化的。然而,该框架确实提供了我们通过 FacesContext 对象访问 HTTP 会话所需的所有信息
public class GlobalevEventPhaseListener implements PhaseListener {
public void beforePhase(final PhaseEvent event) {
final FacesContext facesContext = event.getFacesContext();
final HttpSession httpSession =
JSFUtil.getHttpSession(facesContext);
if (httpSession != null) {
final List globalEvents = getGlobalEvents(httpSession);
if (!globalEvents.isEmpty()) {
fireEvents(globalEvents);
}
}
}
private void fireEvents(final List globalEvents) {
final BeanManager beanManager = lookBeanManager();
if (beanManager != null) {
try {
for (final GlobalEvent devaGlobalEvent : globalEvents) {
beanManager.fireEvent(devaGlobalEvent);
}
} catch (final Exception e) {
throw new IllegalStateException("fireEvents", e);
}
}
}
@Override
public PhaseId getPhaseId() {
return PhaseId.RENDER_RESPONSE; // RESTORE_VIEW;
}
private BeanManager lookBeanManager() {
try {
final Object obj =
new InitialContext().lookup("java:comp/BeanManager");
return (BeanManager) obj;
} catch (final Exception e) {
throw new
IllegalStateException("Lookup bean manager", e);
}
return null;
}
private synchronized List getGlobalEvents(final HttpSession httpSession) {
final List events = (List) httpSession.getAttribute(
GlobalevHttpSessionController.EVENT_ATTRIBUTE_NAME);
final List result = new ArrayList();
if (events != null) {
result.addAll(events);
events.clear();
}
return result;
}
}
最后,我们需要在 faces-config.xml 中注册我们的监听器:
<lifecycle>
<phase-listener>
de.akquinet.jbosscc.globalev.listener.GlobalevEventPhaseListener
</phase-listener>
</lifecycle>
代码也可以在github 上找到
感谢马库斯!