2
votes

I have a simple setup with just one single Session that backs a JSF xhtml file. Within that I fire an event, expeting that both the same session, and any other session will receive the event on submitting.

However, oddly enough, I can see that during the firing of the event, only the current session receives it, not any other sessions. I am making sure I have two different sessions by using two different browsers (Safari and Firefox in this case).

Do I have the concept of CDI based Events wrong?

The bean backing the session:

package testevent;

import java.io.Serializable;
import javax.enterprise.context.SessionScoped;
import javax.enterprise.event.Event;
import javax.enterprise.event.Observes;
import javax.enterprise.event.Reception;
import javax.inject.Inject;
import javax.inject.Named;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

@Named
@SessionScoped
public class TestEventSession implements Serializable {
  private String message = "Start Message";
  private String receivedMessage = "";
  
  @Inject
  @ForTest Event<String> messageEvent;
  
  Logger LOG = LogManager.getLogger();
  
  public void messageChanged(@Observes(notifyObserver = Reception.IF_EXISTS) @ForTest String message) {
    LOG.info("messageChanged <-- "+message);
    this.receivedMessage = message;
  }

  public String getReceivedMessage() {
    return receivedMessage;
  }
  
  public String getMessage() {
    LOG.info("getMessage --> "+message);
    return message;
  }

  public void setMessage(String message) {
    LOG.info("setMessage <-- "+message);
    this.message = message;
    LOG.info("Firing Message Change");
    messageEvent.fire(message);
    LOG.info("Done Firing Message Change");
  }
}

The xhtml file:

<?xml version='1.0' encoding='UTF-8' ?> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html 
  xmlns="http://www.w3.org/1999/xhtml"
  xmlns:h="http://xmlns.jcp.org/jsf/html"
  xmlns:f="http://java.sun.com/jsf/core"
  >
  <f:view transient="false">
    <h:body>
      <h:form>
        <h:inputText value="#{testEventSession.message}" />
        <h:outputText value="#{testEventSession.receivedMessage}" />
        <h:commandButton value="Submit"/>
        <h:button value="Refresh" />
      </h:form>
    </h:body>
  </f:view>
</html>

The Qualifier used is a very basic one:

package testevent;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.inject.Qualifier;

@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface ForTest {
}

As part of the debugging output, you can see that only the observer method in the current thread/session receives the event:

10:45:11.213 [GUEST] [http-nio-8080-exec-32] INFO  testevent.TestEventSession - Firing Message Change
10:45:11.213 [GUEST] [http-nio-8080-exec-32] INFO  testevent.TestEventSession - messageChanged <-- Start Message #3
10:45:11.213 [GUEST] [http-nio-8080-exec-32] INFO  testevent.TestEventSession - Done Firing Message Change

Note that this has been tested with TomEE 7.0.3, which uses assumingely OpenWebBeans 1.7.3.

Related

Update

It might be that this is an intended feature, however I could not find this explicitly stated in the CDI-Spec. Although the provided example seems to be making sense only when the Observer only Observes fired events within the same session. In the case of Produces it clearly states the inter-workings with scoped beans. I think it is rather some documentation lagging in the official spec.

So far other solutions seem to be:

  • Manually registering and tracking session beans
  • Use of JMS / Message Driven Beans (MDB).
1
This issue is similar to this, where event is fired from ApplicationScoped to SessionScoped - stackoverflow.com/q/4481921/744133. Mine is only different as it fires from SessionScoped to SessionScoped.YoYo

1 Answers

4
votes

On observer side only currently active session will catch it by spec. If you want to broadcast on all sessions you need to keep track of them in a registry.

5.5.6 of CDI 2.0:

Obtain a contextual instance of the bean which declares the observer method according to Contextual instance of a bean.

The contextual instance is defined in 6.5.3 as

the instance served by the context of the current scope.