EJB 3.0 Post Construct
I want to discuss some insight of the EJB 3.0 life cycle, in particular about the @PostConstruct annotated methods.
From JSR-220 5.1.4.1:
"PostConstruct methods are invoked on the newly constructed instance, after any dependency injection has been performed by the container and before the first business method is invoked on the bean."
this can be quite good with the old EJB 2.1 specs, but with the new 3.0 environment and pervasive Dependency Injection, this thing astonished me. Moreover I was astonished also by the fact that one of the official Bea's WLS 10.0 Samples contains this kind of code (frin %BEA_HOME%\bea101\wlserver_10.0\samples\server\examples\src\examples\ejb\ejb30\src\java\examples\ejb\ejb30\mdb\WatchProcessMDB.java):
@PersistenceContext(unitName = "reviewSession")
private EntityManager em;
private SubscriptionService service;
@PostConstruct
private void init(){
service = new SubscriptionService();
// inject the EntityManager;
service.injectEntityManager(em);
}
I suddenly asked myself how this can work. In fact in this EJB's business methods the POJO's SubscriptionService methods are called and they use the EntityManager injected in the PostConstruct phase (look at the init()).
In my remembering from EJB < 3.0 the EJB's life cycle was that objects in pool wasn't pure instantiated classes, but ready-to-use bean, and so with the PostConstruct method called. This mean that we could do every "heavyweigth" stuff in there that can be shared across calls of the same bean instance. Then the container was responsible of associating a "working" instance of the Bean at every client call, without doing the PostConstruct again.
It's clear that this semantic is a bit in contrast with the specs sentence, and totally incompatible with Bea's code sample (remember that EntityManager is a representation of an ORM session, so with persistence object cache and DB connection).
To clearly understand things I organized a little test: a MDB that calls a Stateless, the Stateless that use the EM, then I print was is injected by the container in the PostConstruct phase and what in business methods.
This is the sample code:
@Local(SessionBeanLocal.class)
@Stateless
public class SessionBean implements SessionBeanLocal {
@PersistenceContext(unitName="testUnit")
EntityManager em;
@Resource
SessionContext sessionContext;
@PostConstruct
public void init() {
System.out.println("SessionBean: "+this.toString());
System.out.println("SessionContext: "+sessionContext.toString());
System.out.println("EntityManager: "+em.toString());
}
public String test(String input) {
System.out.println("em: "+em.toString());
}
}
@MessageDriven(...)
public class TestMDBBean implements MessageListener {
@Resource
MessageDrivenContext context;
@EJB SessionBeanLocal sessionBean;
@PostConstruct
public void init() {
System.out.println("TestMDBBean: "+this.toString());
System.out.println("MessageDrivenContext: "+context.toString());
}
public void onMessage(Message message) {
try {
TextMessage textMessage = (TextMessage) message;
System.err.println("before: "+textMessage.getText());
System.err.println("after: "+sessionBean.test(textMessage.getText()));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
e questo l'output del log dopo qualche chiamata:
<28-feb-2008 10.03.14 CET> <Notice> <Stdout> <BEA-000000> <TestMDBBean: test.ejb.mdb.TestMDBBean@829c0b>
<28-feb-2008 10.03.14 CET> <Notice> <Stdout> <BEA-000000> <MessageDrivenContext: weblogic.ejb.container.internal.MessageDrivenEJBContextImpl@d2d710>
before: test____2
<28-feb-2008 10.03.14 CET> <Notice> <Stdout> <BEA-000000> <SessionBean: test.ejb.mdb.SessionBean_7pp7ls_Impl@cc564d>
<28-feb-2008 10.03.14 CET> <Notice> <Stdout> <BEA-000000> <SessionContext: weblogic.ejb.container.deployer.SessionContextProxyImpl@135f763>
<28-feb-2008 10.03.14 CET> <Info> <Kodo> <BEA-2004002> <Starting BEA Kodo 4.1.3load02>
<28-feb-2008 10.03.14 CET> <Info> <Kodo JDBC> <BEA-2002003> <[JDBC] OpenJPA will now connect to the database to attempt to determine what type of database dictionary to use. To prevent this connection in the future, set your org.apache.openjpa.jdbc.DBDictionary configuration property to the appropriate value for your database (see the documentation for available values).>
<28-feb-2008 10.03.14 CET> <Info> <Kodo JDBC> <BEA-2002382> <[JDBC] Using dictionary class "org.apache.openjpa.jdbc.sql.OracleDictionary" (Oracle 10.1.0.3.0 ,Oracle 3.50.63 (016256.009419.012852)).>
<28-feb-2008 10.03.15 CET> <Notice> <Stdout> <BEA-000000> <EntityManager: org.apache.openjpa.persistence.EntityManagerImpl@14c502d>
after: $test____2$
<28-feb-2008 10.03.15 CET> <Notice> <Stdout> <BEA-000000> <em: org.apache.openjpa.persistence.EntityManagerImpl@14c502d>
<28-feb-2008 10.03.29 CET> <Notice> <Stdout> <BEA-000000> <em: org.apache.openjpa.persistence.EntityManagerImpl@c09423>
before: test____2
after: $test____2$
<28-feb-2008 10.03.32 CET> <Notice> <Stdout> <BEA-000000> <em: org.apache.openjpa.persistence.EntityManagerImpl@1d8b3c7>
before: test____2
after: $test____2$
before: test____2
after: $test____2$
<28-feb-2008 10.03.33 CET> <Notice> <Stdout> <BEA-000000> <em: org.apache.openjpa.persistence.EntityManagerImpl@19b37f6>
I sent two messages with "test____" and three with "test____2".
We can understand that:
1) Obviously, before calling the MDB's onMessage the container calls the init method and all resources are correctly injected.
2) Before calling the SLSB init, all the resources are injected and the EM instance is @14c502d.
3) At the first call to the business method "test" the EM instance is again @14c502d, so everything is ok, Bea's code work fine !!! WLS threat the PostConstruct's "init" method and the "test" method as in the same transaction started from the MDB.
But at subsequent calls we can see that the EM instance is always different (thanks to god !!!!!), and this means that:
1) Bea's EJB 3.0 code sample is WRONG !!!
2) Specs are good (in effect the EM is correctly initialized when the init method is called), but doesn't explicitly say that resources "transaction dependent" like the EM will be again injected in new transaction business methods call.
May be the key is in this sentence, always from EJB 3.0 Specs:
"PostConstruct methods are invoked in an unspecified transaction and security context."
but I'm not so sure, I think that all this can be an heritage of the old EJB 2.1, when DI was not implemented by containers.