I'm facing a problem with a conditional observer method that is not being called. Here is the code, starting with a junit test:
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;
import javax.enterprise.inject.Instance;
import javax.enterprise.inject.se.SeContainer;
import javax.enterprise.inject.se.SeContainerInitializer;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class CDIMinimalConditionalObserverTest
{
private final static Logger LOGGER = LogManager.getLogger(CDIMinimalConditionalObserverTest.class);
private SeContainer container;
@Before public void before()
{
LOGGER.debug("before");
final SeContainerInitializer initialiser = SeContainerInitializer.newInstance();
container = initialiser.initialize();
}
@After public void after()
{
container.close();
LOGGER.debug("after");
}
@Test public void testObservation_observationInManagedNonExistentConditionalObservers()
{
CDIMinimalConditionalObserverEvent event = new CDIMinimalConditionalObserverEvent();
container.getBeanManager().fireEvent(event);
assertThat(event.msg, nullValue());
}
@Test public void testObservation_observationInManagedExistentConditionalObservers()
{
// create observer by selection
Instance<CDIMinimalConditionalObserver> instance = container.select(CDIMinimalConditionalObserver.class);
CDIMinimalConditionalObserver observer = instance.get();
assertThat(observer, notNullValue());
CDIMinimalConditionalObserverEvent event = new CDIMinimalConditionalObserverEvent();
container.getBeanManager().fireEvent(event);
observer.doSomething();
assertThat(event.msg, notNullValue());
}
}
Here is the class with the conditional observer method:
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.enterprise.event.Reception;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import de.jmda.sandbox.cdi.se.CDIMinimalTests.SimpleInnerEvent;
/**
* {@link Model} annotation assigns non-dependent scope and thereby makes it possible to make {@link
* #observation(SimpleInnerEvent)} conditional
*/
@ApplicationScoped public class CDIMinimalConditionalObserver
{
private final static Logger LOGGER = LogManager.getLogger(SimpleConditionalObserver.class);
public CDIMinimalConditionalObserver()
{
LOGGER.debug("constructor");
}
@PostConstruct public void postConstruct()
{
LOGGER.debug("post construct");
}
@PreDestroy public void preDestroy()
{
LOGGER.debug("pre destroy");
}
public void observation(@Observes(notifyObserver=Reception.IF_EXISTS) CDIMinimalConditionalObserverEvent event)
{
event.msg = "observation";
LOGGER.debug(event.msg);
}
public void doSomething()
{
LOGGER.debug("doing something");
}
}
And finally here is the event class:
public class CDIMinimalConditionalObserverEvent { String msg; }
The test fails because event.msg is null though it shouldn't be. Logging output does not show any "observation" output. The test passes if the condition is removed.
Any ideas? Thanks!
When your
@ApplicationScoped
Bean gets discovered, it does not get instantiated immediately.CDI
is smart enough to initialize the real object, the one behind the scene, only when needed.You can see that retrieving an
ApplicationScoped
Bean viaDoes indeed return a proxy instance.
At this stage there is still no
App
Bean attached to the Application context.Now, try to interact with that object (even just by calling
toString
), and only after, fire the event.You'll notice it does work, because the underlying object has been instantiated via its no-arg constructor.
Removing
Reception.IF_EXISTS
simply signalCDI
that it has to create and attach to the context the underlying instance immediatly, so that it can accept incoming events no matter what.This proxying behavior is documented in the specification (I need to find the page), and it's why a Bean requires a no-arg constructor.
Dependent
scope Beans don't suffer of this problem, as they're created every time it is needed, from scratch, and are not tracked by the framework. For singleton, session, or request scoped Beans, a proxy is required to manage them correctly.Dependent
scope Bean, you can see it's a "pure" instance