MarkusDahmがブログエントリで説明しているソリューション
Webアプリケーションのすべてのアクティブなセッションにイベントを送信する彼のソリューションには、グローバルイベントをサポートするために既存のCDIオブザーバーパターンを拡張することが含まれます。
関連するコードは次のとおりです。
まず、すべてのHTTPセッションを登録して処理し、すべてのイベントをセッションに委任するGlobalevHttpSessionControllerが必要です。
@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());
}
}
そして、クライアントにイベントをディスパッチするために、彼はJSFPhaseListenerを使用して着信イベントを検索することを選択しました。たとえばフィルターを使用する他の解決策も可能です。リスナーはグローバルイベントを探し、CDIBeanマネージャーを使用してそれらをローカルセッションにディスパッチします。マネージャインスタンスは、JNDIルックアップを介して取得されます(仕様により、Beanマネージャはjava:comp / BeanManagerの下のコンテナによってバインドされる必要があります)。残念ながら、フェーズリスナーはCDIではなく、Java Server Faces(JSF)フレームワークによってインスタンス化されるため、ここではCDIインジェクションを使用できません。ただし、フレームワークは、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でも入手できます
Markusに感謝します!