Skip to main content

Persistence Context propagation

Posted by ss141213 on October 27, 2006 at 11:01 AM PDT

What is persistence context propagation?
A persistence context(PC) is a set of managed entity instances in which for any persistent entity identity there is a unique entity instance (this definition is taken from Java Persistence API spec, section 5.1). An Entity Manager(EM) provides the necesarry APIs to interact with a persistence context.
There is often a need to access the entities in the same PC in different components (say multiple EJBs or web component and EJB etc.) involved in processing of a request. One way of doing this is to pass an EM instance around in the call stack from one component to another, but that's not so elegant as it pollutes the interfaces. A better and recommended approach is to use a feature called Persistence Context Propagation that is offered by the Java EE container. PC propagation allows multiple components of a Java EE application to share the same persistence context. Each component use their own EM which they obtain using dependency injection or JNDI lookup and when they use the EM in the contex of the same JTA transaction, container automaticaly binds the EM to the same PC. Thus application does not have to explicitly pass an EM from one component to another. This technique is portable across any Java EE 5 compatible application server.

Rules governing PC propagation
1. Only a PC associated with a container managed EM can be propagated.

2. A PC is propagated along with the JTA transaction.

3. PC propagation is applicable only to local environments(i.e. one JVM). PC is not propagated across remote tiers even if the remote component is collocated in the same address space as the client.

4. A PC is propagated when the called component uses EM that belongs to the same Entity Manager Factory as the caller.

5. It is an application error to attempt to propagate a PC to a SFSB that is already associated with a different PC. In such a case, EJB container throws EJBException to the caller.

Let's apply the rules (rule shown in ()) and see some cases where PC propagation takes place:

1. (rule #1) Since a container managed EM can only be obatined via @PersistenceContext or persistence-context-ref, PC propagation does not take place for EMs obtained using EntityManagerFactory.createEntityManager().

2. (rule #2) A PC won't be propagated (e.g.) when an EJB with bean managed transaction or an EJB method with transaction attribute REQUIRES_NEW, NOT_SUPPORTED or NEVER is called because transactions are not propagated to such methods(see Chapter #13 of EJB 3.0 Core specification). More over, unless the caller is associated with a transaction, PC propagation will not happen. I don't know the behavior when a request is dispatched from one web component to another using using RequstDispatcher object. There are two questions that determine the answer, viz: a) is that ever considered a local call? b) does web container propagates the JTA transaction along with the request? If you know the answer, let me know. Else, I will try to find out myself.

3. (rule #3) Don't expect PC to be propagated when you call a remote EJB even when the remote EJB happens to be running in the same JVM or part of the same application.

4. (rule #4) All the components that want to share the PC must use the same persistence unit. Container internally creates an EntityManagerFactory corresponding to a persistence unit and uses it to create EMs. Persistence units defined in separate persistence.xml files or persistence units defined separately in one persistence.xml file are considered different no matter how idenically they are set up. So, if components from two different modules in an EAR file (e.g. a Servlet calling an EJB) want to share PC, then the persistence unit must be packaged as an EAR scoped persistence unit (e.g. in a jar file in lib folder of the EAR).

5. (rule #5) If a component is associated with a PC then it can't call a local SFSB with a different extended PC to which current transaction context will be propagated. GlassFish reports this very nice message when I tried doing this in a test case: javax.ejb.EJBException: There is an active transactional persistence context for the same EntityManagerFactory as the current stateful session bean's extended persistence context. (rule #5)

These rules are not complete. Please refer to the JPA spec for more details.

Sample Application
Let's now use this feature in a simple Java EE application. In this example, a PC is shared between ReportServlet and UserCredentialManagerBean. I must say this is a bad example of PC propagation, but I just could not think of a better example for this article. The source code and build script can be downloaded from here. Instructions to build and run are available in the README. There are lots of System.out.println statements in the code that gives an insight into what is happening. All those messages go to the $GLASSFISH_HOME/domains/domain1/logs/server.log by default. Although I am using GlassFish which is the reference implementation of Java EE 5 platform, you can use it in any Java EE 5 compatible application server.
Let's now look at the relevant sample code:

1. ReportServlet.java
:
Since a servlet should not be injected with a PC, ReportServlet gets hold of a container managed EM by looking up JNDI environment as shown below:

        EntityManager em = (EntityManager) new InitialContext().lookup(
                    "java:comp/env/persistence/EM1");

The above look up returns a valid EM because the Servlet has declared a dependency on a PC for the persistence unit named pu1 and has given this dependency a name persistence/EM1 using the following code:

@PersistenceContext(name="persistence/EM1", unitName="pu1")
public class ReportServlet extends HttpServlet {

ReportServlet begins a transaction using the injected UserTransaction object, obtains a managed entity instance by invoking lookup method of injected UserCredentialManager session bean, accesses the lazily fetched field of the entity and then commits the transaction in the code below:

        utx.begin();
        UserCredential user = ucm.lookupUser(name);
        for(LoginAttempt attempt : user.getLoginAttempts()) {...}
        utx.commit();

2. UserCredentialManagerBean.java:
This is a stateless session bean with container managed transaction(this is the default). It has a local business interface called UserCredentialManager. It uses an injected EM. The transaction attribute for lookup method is REQUIRED (default).

3. Persistence Unit:
There is an EAR scoped persistence unit called pu1 which is defined in lib/entities.jar. Both the servlet and the session bean reference this persistence unit. There are two entity beans, viz UserCredential.java and LoginAttempt.java

You may have noticed that ReportServlet does not use the EM in any meaningful way. So, it can be totally removed from ReportServlet and yet ReportServlet will be able to use the entity in the same PC as that of the EJB.

Let's see what would have happened if ReportServlet does not start the transaction before calling lookup method. Lookup method starts a new transaction and EJB container creates a new PC when the injected EM is used. This PC gets closed automatically when the transaction is ended at the end of that method, thus the returned entity becomes detached. Since fetch type for loginAttempts is LAZY (by default it is lazy for collection valued fields) and the field has not been accessed by lookup method, accessing it in the Servlet is asking for trouble (see section #3.2.4 of the JPA spec). GlassFish persistence provider is generous enough to allow such an access, but when I tested using OpenJPA, it threw a NullPointerException.

Conclusion
Hope you find this useful.

Resources
Packaging of Java EE application that uses JPA
Plugging in a thirdparty provider in GlassFish
More blogs about

Related Topics >>