Arch2Arch Tab BEA.com
Syndicate this blog (XML)

How EJB 3 and JPA Programming Model speed up and simplify N-tier Web Application Development

Bookmark Blog Post

del.icio.us del.icio.us
Digg Digg
DZone DZone
Furl Furl
Reddit Reddit

Pinaki Poddar's Blog | May 25, 2006   1:06 AM | Comments (5)


EJB 3 JPA Programming Model Simplicity is one of the most important drivers of new EJB 3 specification, in particular, and Java EE 5 platform, in general. New POJO based EJB 3 and delegation of persistence services to a distinct Java Persistence API (JPA) interface open up excellent opportunities for the bean developer to simplify and quicken the entire code-test-debug cycle.

New technology innovations demand new thinking, new development practices. This blog explores a service-oriented development pattern to speed up code-test-debug cycle to build multi-tier applications.

Let us consider a typical business method for illustrative purpose


public interface ReviewService
{
	 /**
	  *  Creates a new Review by the given reviewer for the given item.
	  *
	  * @param reviewerName name of the reviewer. The named reviewer must exist.
	  * @param itemId unique identifier of the item being reviewed. The item must
	  * exist.
	  * @param rating an integer between BEST_RATING and WORST_RATING 
	  * @param comment a free form text as a comment
	  *
	  * @return newly created Review
	  * @exception IllegalArgumentException if the named reviewer or item does not
	  * exist. Or if the rating has non-permissible value.
	  * @exception NullPointerException if comment is null or empty
	  *
	 **/
	Review newReview (String reviewerName, String itemId, 
			int rating, String comment);

We want a Stateless EJB 3 Stateless Session Bean to implement this method. So we go cranking on our keyboard

 01 @Stateless
 02 @Remote(ReviewService.class)
 03 public class ReviewSessionBean implements ReviewService
 04 {	
 05 	@TransactionAttribute(REQUIRED)
 06 	public Review newReview (String reviewerName, String itemId, 
 07 			int rating, String comment) 
 08   {
 09 		Item item          = em.find(Item.class,itemId);
 10 		Reviewer reviewer  = em.find(Reviewer.class, reviewerName);
 11 		if (item==null)
 12 			throw new IllegalArgumentException("No item with id [" + itemId + "]");
 13 		if (reviewer==null)
 14 			throw new IllegalArgumentException("No reviewer named [" + reviewerName + "]");
 15 		if (rating < WORST_RATING || rating > BEST_RATING)
 16 			throw new IllegalArgumentException ("Illegal rating " + rating + 
 17 					". Must be between " + WORST_RATING + " and " + BEST_RATING);
 18 		if (comment==null || comment.trim().length()==0)
 19 			throw new NullPointerException("Null or empty comment is not allowed");
 20 		
 21 		Review review = reviewer.review(item, rating, comment);
 22 		em.persist(review);
 23 		
 24 		return review;
 25 	}
 26 	@PersistenceContext(unitName="ejax.session")
 27 	private EntityManager em;
Few points to note on above code lines:  A. What is em? That is javax.persistence.EntityManager injected by the EJB container. The instruction for the container to inject is in the following two lines of code

 26 	@PersistenceContext(unitName="ejax.session")
 27 	private EntityManager em;
   
This is dependency injection or inversion of control design pattern at work for you in Java EE 5 (if you care to impress with words).

 B. No explicit transaction begin or commit.
The method-level annotation


 05 	@TransactionAttribute(REQUIRED)
   
tells the container, before invoking the session bean's method, not only to inject an EntityManager to the method, but also to ensure that the EntityManager is in a active transaction. And, after the bean's method completes, the container commits the transaction.

 C. What kind of transaction?
Depends on what was specified as the value of transaction-type attribute in persistence unit named ejax.session. The choices are JTA and RESOURCE_LOCAL and for the given example, the persistence unit is specified in META-INF/persistence.xml as


      	<persistence-unit name="ejax.session" transaction-type="JTA">
   

New Technology demands new thinking

No doubt, this Session Bean is written using new EJB 3 technology. But with old thinking, with old development pattern. Why do I say that? Before further explanation, let us note some observations on these typical lines of code
  • Observation 1:
  • A business method is an interplay between business logic and infrastructure support services. The correct execution of the business function depends on business semantics, e.g.
      -- does Reviewer.review() method works correctly?
      -- does the method throw a exception if a negative rating is passed?
    as well as infrastuctural services
      -- does the correct EntityManager get injected with an active transaction?
      -- does the new Review instance committed to the datastore?

  • Observation 2:
  • The correctness of the first category is the bean developer's concern, while correctness of the second category is that of the infrastucture provider's. Of course, The bean developer/deployer must verify that the infrastructure services are specified correctly. For example, persistence-unit name in META-INF/persistence.xml "ejax.session" is consistent with the annotation for the injected EntityManager
     26  @PersistenceContext(unitName="ejax.session")
    which when writing this example, I goofed up as follows
    @PersistenceContext(name="ejax.session")
    The qualifier name on @PersistenceContext annotation implies JNDI name of the instance, whereas unitName refers to persistence unit name to look for. The point is, if the deployment descriptors are correctly specified the application server is expected to do its part -- the bean developer just verifies that the descriptors are correct.
    However, the way the bean method is coded it does not separate the distinction between testing the business semantics versus verification of deployment descriptions.
    The implementation pattern followed leaves the only option to tested is by deploying this session bean in application container and then perhaps finding out that there is a silly error such as
    
    		if (rating > WORST_RATING || rating < BEST_RATING)
       
    even if you detect the mistake in a moment once a lengthy stack trace is dumped on your console, you then need to edit, compile, package, deploy, test and verify that the error is eliminated.

    Separate business logic and infrasturture service

    The design goal is to separate the business logic implementation from the application server dependency. New EJB 3 already took a major step towards this simplicity by defining Entity Java Beans as POJO. Also persistence services are identified as a separte service API that works with or without container support as well.
    The current design strategy extends the similar theme to capture the business logic as POJO-based service. Once the POJO based service is tested without a container in a quicker code-test-debug cycle, the tested business service is stiched back into the original stateless Session Bean that would act as a mediator between the application server's infrastructural services such as dependency injection, transaction control, multi-threading, remoting. The strategy is depicted in the following diagram:


    The business interface ReviewService is implemented in a Java class ReviewServiceImpl independent of infrastructural issues. This way, the business logic is available to a Web Service, a Session Bean or a Swing GUI button's action handler -- as a business service developer one makes absolutely no assumption. That is the hallmark of a good service design -- being agnostic of the caller. This is also known as selfish programming.
    This new service-oriented, infrastructure-independent implementation of the same business method looks like as follows
       
     01 public class ReviewServiceImpl 
     02        extends PersistenceService
     03        implements ReviewService
     04 {	
     05 	public Review newReview (String reviewerName, String itemId, 
     06 			int rating, String comment) 
     07   {
     08       EntityManager em   = beginTransaction();
     09 		Item item          = em.find(Item.class,itemId);
     10 		Reviewer reviewer  = em.find(Reviewer.class, reviewerName);
     11 		if (item==null)
     12 			throw new IllegalArgumentException("No item with id [" + itemId + "]");
     13 		if (reviewer==null)
     14 			throw new IllegalArgumentException("No reviewer named [" + reviewerName + "]");
     15 		if (rating < WORST_RATING || rating > BEST_RATING)
     16 			throw new IllegalArgumentException ("Illegal rating " + rating + 
     17 					". Must be between " + WORST_RATING + " and " + BEST_RATING);
     18 		if (comment==null || comment.trim().length()==0)
     19 			throw new NullPointerException("Null or empty comment is not allowed");
     20 		
     21 		Review review = reviewer.review(item, rating, comment);
     22 		em.persist(review);
     23       commit();
     24 		
     25 		return review;
     26 	}
     
    If we compare this new implementation against the original stateless Session Bean we see
    1. The class level annotations @Stateless and @Remote are gone.
    2. The method level annotations @TransactionAttribute(REQUIRED) is removed
    3. Depedency injection of EntityManager is removed.
    And, new explicit transaction demarcation is introduced through beginTransaction() and commit().
    Effectively, all the annotations that are effectively instructions to the container are removed and a mechanics is provided to transaction control that is critical for correct operation of the business method.

    Advantage of separating business logic from infrastructure services

    What does this new orientation towards separting the business logic from infrasturucre services buy us? We can test the business logic (to certain degree of certainity) outside a container. This, in turn, significantly speeds up the code-test-debug cycle and minimizes resource requirements.
    But the story is not over. We got rid of the container services from the implementation. But have we thrown the baby away with the bath water? How would we ensure that the business method has access to a EntityManager to perform its duties?
    First reaction is to get the same persistence unit, create a EntityManagerFactory that would supply us the EntityManager we need.
    But we want more. Our original target is to make the service available via a stateless Session Bean that is injected with EntityManager. So what we need to satisfy the dual (and slightly confronting) requirements of a) separting the business functions from infrasturucture for rapid development cycle and b) creating a target solution that harnesses the containers infrastructure with business logic. The solution of being two things at the same time is realized in PersistenceService - a class with dual personality. This class can control the transaction explicitly or relinquish its transaction responsibility to the container depending on its invocation context. The ReviewServiceImpl extends from it and uses its dual transaction control methods beginTransaction() and commit.
    Here is the implementation of PersistenceService.commit() method
    
    	protected final void commit()
    	{
    		if (isManaged())
    			return;
    		EntityManager em = getEntityManager();
    		if (em.getTransaction().isActive()) 
    			em.getTransaction().commit();
    	}
    
    The class is aware whether it is under managed environment. If it is, it relinquishes its transaction control and its commit() effectively becomes a no-op. Otherwise, it acts on its own and commits the transaction.

    How does it detect whether it is in a managed transaction? If it is injected with a EntityManager it considers itself managed otherwise it is on its own. It accomplishes this logic via the following methods

    
    	private final EntityManagerFactory             _emf;
    	private final ThreadLocal<EntityManager> _thread;
    	private volatile boolean                       _isManaged;
    	protected synchronized EntityManager getEntityManager()
    	{
    		EntityManager em = _thread.get();
    		if (em!=null)
    			return em;
    		else if (_emf!=null) 
    		{
    			em = _emf.createEntityManager();
    			_thread.set(em);
    			return em;
    		} 
    		else 
    			throw new NullPointerException("no-entity-manager");
    	}
    	protected synchronized void injectEntityManager (EntityManager em)
    	{
    		_isManaged = true;
    		_thread.set(em);
    	}
    	public final boolean isManaged() 
    	{
    		return _isManaged;
    	}
    
    
    It maintains a per-thread EntityManager in ThreadLocal variable _thread. A call to its getEntityManager method supplies a EntityManager that is either injected earlier through injectEntityManager() (i.e. it is in managed state when it relinquishes its transactional duties) or a EntityManager created from the EntityManagerFactory on a per-thread basis. Finally, its beginTransaction() method is a combination of getEntityManager and EntityManager.getTransaction().begin() methods.
    
    	protected final EntityManager beginTransaction()
    	{
    		EntityManager em = getEntityManager();
    		if (!isManaged() && !em.getTransaction().isActive()) 
    			em.getTransaction().begin();
    		return em;
    	}
    

    Put the humpty-dumpty back gain

    We have isolated the business contract of ReviewService in ReviewServiceImpl from original stateless Session Bean ReviewSessionBean.
    We figured out a technique to supply EntityManager within and outside a container through a single abstraction of PersistenceService.
    Let us now reconstruct the original Session Bean in terms of these facilities
    
     01 @Stateless
     02 @Remote(ReviewService.class)
     03 public class ReviewSessionBean
     04 	extends ReviewServiceImpl
     05 	implements ReviewService
     06 {	
     07 	@TransactionAttribute(REQUIRES_NEW)
     08 	public Review newReview (Reviewer reviewer, Item item, 
     09 			int rating, String comment) 
     10 	{
     11 		injectEntityManager(em);
     12 		return super.newReview(reviewer, item, rating, comment);
     13 	}
     14 	@PersistenceContext(unitName="ejax.session")
     15 	private EntityManager em;
     16 }
    
    What is done in this refactored version is
    1. the Session Bean extends the business service implementation ReviewServiceImpl which effectively implements core external contract of ReviewService.
    2. the infrastructure service annotations (@Stateless, @Remote, @PersistenceContext) are reinstituted.
    3. the injected EntityManager is reinjected into the super class PersistenceService which signals to behave it in managed context.
    4. finally, the business function is realized thorough the implementation in the super class.
    We realize our strategy through inheritence but it could as well have been realized through delegation.

    JUnit Test Cases

    The JUnit Test cases align in the same inheritance hierarchy. Two JUnit Test cases TestService and TestSession both test the same interface ReviewService. But while the former invokes an instance of ReviewServiceImpl through direct construction, the later connects via JNDI lookup to a ReviewSessionBean. In fact, TestSession only implements its setUp() to connect to the remote Session Bean, its test methods are already implemented in its super class TestService.

    Summary

    The new EJB 3 and JPA bring simplicity to development. This discussion follows the same theme to simplify code-test-debug cycle for Session Bean through a dual transaction control design pattern and code examples.
    In the next blog, I will discuss another practical issue: how to switch databases in different testing environment?


    Good Night and Good Luck

    Comments

    Comments are listed in date ascending order (oldest first) | Post Comment

    • I'm new to EJB 3 and just testing it now. Here I have some concerns about it. 1. Can JPA decouple with session bean? Till now all the code I read is put JPA and session bean in one module (commonly is EAR). But from the aspect of EJB, the better idea is spitting them aside, entity bean in one EJB modue, and session bean in another one, and any entity bean can be accessed by JNDI. I don't know how EJB 3 can implement it. Now I tried to put JPA POJO in a optional package, and be accessed by standalone session bean module. It works well, but the accesser must hold persistance.xml for the JPA. Obviously this way is ugly, but I can not find a better practice. 2. How can JPA utilize data sources of WLS? Till now I find in all the example code, database detail should be included inside the persistance.xml. I think JPA must use JDBC driectly, without container data source. Sure I know JPA can run standalone, but inside WLS, the better idea is using existing common data source service. Do you have any idea of it? Any way I'm glad to get your article. For any replay, please mail to mwang@bea.com. Thanks!

      Posted by: goblinize on June 7, 2007 at 2:25 AM

    • Hi Pinaki, I'm going through your blog on using EJB3/JPA programming model, in that you've implemented the ReviewServiceImpl extends PersistenceService. Instead of extending it if I make the PersistenceService as an attribute in ReviewServiceImpl, would that break the pattern? Business services has business logic in it and the PersistenceService has the infrastructure logic in it. Not sure if this works without any issues both inside and outside the EJB container? Also you've synchronized the injectEntityManager and getEntityManager methods in PersistenceService, does it really necessary? How would the performance affect in high volume traffic?

      Posted by: mani0001 on November 14, 2007 at 2:56 PM

    • 1. could have reduced the granularity of the lock from method level to class variable level.

      2. The main purpose of PersistenceService is to arrive at a pattern that allows business logic of a JPA application to decouple from infrastructure logic (i.e. where to get a EntityManagerFactory, or pass the same EntityManager along the thread of control) such that testing in JSE and JEE environment.
      I doubt its contribution (positive or negative) in production (i.e. high-volume traffic) environment within application container because, in JEE environment it is designed to be essentially a no-op.

      Posted by: pinaki.poddar on November 15, 2007 at 8:35 PM

    • > Can JPA decouple with session bean?
      Yes. You can create a separate ejb module containing the domain classes and JPA configuration META-INF/persistence.xml file. In fact, one of blog entries do show an example (with source code and Ant script) of the same. Unfortunately, I do not recall exactly which one and one has to scan my ramblings that are accompanied by source code download.

      2. How can JPA utilize data sources of WLS?
      In META-INF/persistence.xml, tag accepts the JNDI name of a registered data source. In fact, there are multiple alternate ways of specifying datastore specifics as documented here:
      http://openjpa.apache.org/docs/latest/manual/manual.html#ref_guide_dbsetup_thirdparty_enlist
      Please note that when a pre-configured datasource is being used under JTA transaction and managed by the application server's transaction manager, there are scenarios when JPA provider will require to access and commit a transaction for its own internal purpose (e.g. when assigning auto-generated identity for a newly persistent object from a database sequence table). As the provider runtime can not commit the connection, one may have to specify as well.

      Posted by: pinaki.poddar on November 15, 2007 at 8:45 PM

    • I doubt its contribution (positive or negative) in production (i.e. high-volume traffic) environment within application container because, in JEE environment it is designed to be essentially a no-op


      I understand that the methods in PersistenceService are "no op" inside the container. My question is since the injectEntityManager and getEntityManager methods are marked as synchronized, does it work properly inside the EJB 3.0 container? Inside the container all the threads are managed by the container, by marking the two methods as synchronized , I hope it shouldn't have any side affects with the container threading mechanism.
      My main goal is, I would like to deploy my business services outside the container (say in Spring framework, or Junit testing) if needed and also inside the EJB 3.0 container. The pattern you described in this blog nicely fits my purpose. I am using EJB 3.0 for the first time, so trying to understand the issues of production implementation of this pattern along with Hibernate, JPA in Weblogic 10.0 app server.

      Posted by: mani001 on November 17, 2007 at 5:53 AM



    Only logged in users may post comments. Login Here.

    Powered by
    Movable Type 3.31