Wednesday, September 07, 2011

JSF + JPA without EJB or Spring

I've had success using JSF 2.0 with JPA on Glassfish 3.0.1, without using Spring or EJB for managing middle-tier services or DAOs. (I actually do like Spring - the goal in this case was to minimize dependencies, moving parts and learning curve, rather than to avoid any particular technology.)

A similar approach was detailed here:
http://www.swview.org/blog/best-way-use-jpa-web-tier

 Using only the JSF @ManagedBean and @ManagedProperty annotations, along with the JPA @PersistenceContext, you can refactor business services or DAOs into other JSF managed beans that are injected into the one exposed directly to the UI. What this looks like in practice:

 orders.xhtml:
 

OrderBean.java:
@ManagedBean
public class OrderBean {
  @ManagedProperty(value="#{orderDao}")
  private OrderDao orderDao;
  private List orders;
   
  @PostConstruct
  public void init() { 
    this.orders = orderDao.getOrders();
  } 
  public List getOrders() { 
    return orders;
  }
}

OrderDao.java:
@ManagedBean
@ApplicationScoped
public class OrderDao {
   @PersistenceContext(...)
   private EntityManager em;
   
   public List<Order> getOrders() { 
     return em.findAll(Order.class);
   }
}
In this example, the DAO (OrderDao) is just another JSF managed bean, but not actually referenced directly from a page, only from another managed bean. This approach lets you isolate the logic tied to EntityManagers from the rest of your "normal" JSF managed beans, and makes it (marginally) easier to unit-test your managed beans because you can mock the DAO at a higher level instead of mocking the entity manager.

 This does NOT buy you declarative transactions or any of the other good stuff you get with Spring or EJB. (You can inject a UserTransaction with @Resource, and call it explicitly, though.) So it works best for simple apps with mostly read operations and basic single-object CRUD transactions.

 Also, with Java EE 6 CDI, all this may become moot because @Named and @Inject annotations effectively blur the line between all the different managed bean flavors (EJB, JSF, Spring/POJO), although I haven't found a good way to replace JSF @ViewScoped without buying into Seam.

4 comments:

Joost said...

One of the main issues I have with using local EJB3.x for all business/dao services, is that I am fixed to using JTA if I want the container to handle the transaction management, and that it gets complicated when ejbs start calling each other. On Websphere, there is also the concept of activity sessions (basically the same as XA but then with a normal single-phase commit on all transactions at the end of the activity session), but its configuration is cumbersome when you want ejbs to call each other (yes, I hate writing XML, I prefer code).
In general, a user clicks a button in a webapp, and then you want all database access to be executed before committing anything. So deciding the transactional boundaries is really the concern of the view/ctrl layer (or at least it is leaking through in some way). It is at that exact point that I want to decide if I want to group all database access into an XA transactions or activity session. Say I have two EJB's with @Requires, and I want to combine them into a single XA transaction: this requires me either to demarcate the XA transaction myself, or to write yet another EJB which calls the two @Requires EJB's. You may end up to write an EJB for each user action. The point I am trying to make is that transaction management starts from the user actions, not from the business service level, and certainly shouldn't blur the DAO's.

Where am I getting at? I have one possible approach by which I only need to write 2 stateless EJB3's, one with @Required and one with @NotSupported. Let's start with the easy one:

@Local interface RunInXA {
public R run(TxRunnable runner, Object ... args);
public R run(TxRunnable1 runner, P p);
// ... maybe some more with more parameters
}
@Stateless RunInXAImpl implements RunInXA {
public run(TxRunnable runner, Object ... args) {
return runner.run(args);
}
public R run(TxRunnable1 runner, P p) {
return runner.run(p);
}
// ...
}
interface TxRunnable { public R run(Object ... args); }
interface TxRunnable1 { public R run(P p); }

With this setup, you can easily execute any logic within an XA transaction by implementing the callback interface and feeding it to the EJB which is going to take care of the transaction management on the borders you exactly want. I can write a similar bean which doesn't use XA, and either use an activity session on that bean on Websphere, or write the for-loop of commits myself (this does require a way to know which resources have been accessed, so is a little bit harder than I make it seem).
Now, my DAO's can be either POJO's or JSF managed beans. When going for the JSF beans, you usually want to make your DAO @ApplicationScoped, hence you have an application singleton, which has one caveat: I can't wire session or request scoped data into it, so I need to pass all such stuff by method parameters, which is a little bit cumbersome due to the use of the callback interface. You could make your DAO @RequestScoped, and pull in anything from the view layer, but this approach really violates MVC.

Unknown said...

@Joost, I agree, DAOs are usually too granular for transactions to make sense as EJBs. The transaction is usually higher-level covering multiple DAO calls, plus mutators (setter methods) that are persisted on commit. With Java EE6 CDI, you could make the DAO a @Named POJO and then @Inject it into an EJB.

Unfortunately, JSF doesn't let you declare transactions on the managed bean itself. (Spring MVC does let you put @Transactional on a @Controller.) Hence you end up needing to split this out into an EJB and pass everything as arguments - cumbersome, as you point out.

Wouldn't it be great if you could put a @Required annotation on a @ManagedBean method, so you could have <h:commandLink actionListener="#{bean.method}"/> and then

@Required
public void method() {...}

?

Too bad JSF doesn't let you.

Joost said...

Bill, you should check out http://adam-bien.com/roller/abien/
his blog. There I learned that you can make a jsf controller both @Stateless and @Named: this makes a call from your jsf page transactional! The scope of such a 'cdi ejb' is then just the default: dependent. You can still inject any scoped CDI bean into the stateless cdi bean A: it works as it should. Example: injecting a request scoped cdi bean B into such an 'cdi ejb', will inject a different newly created B in each request (you may reuse the same A ejb instance). One caveat: don't use A for storing page input: don't use an ejb for this purpose.

Btw: your @Required annotation which surrounds a method call with a transaction is also possible by use of cdi interceptors, see
http://www.blogger.com/comment.g?blogID=5399786441472918987&postID=3345706791335583365&page=1&token=1322686077962

Remark on your example: EntityManager is not thread safe, so injecting it into an application scoped jsf bean is not ok: you can only inject it into a request scoped jsf bean, or preferably an @Stateless ejb (which has more or less the same semantics of request scoped when it comes to thread safety).

Unknown said...

@Joost, good observation about @Stateless + @Named = transactional JSF managed bean.

Also - while a raw EntityManager is not threadsafe, the EntityManager injected via @PersistenceContext is a threadsafe, serializable wrapper that knows how to get the right underlying EntityManager for the current invocation. IMO there should be a different interface to alleviate this confusion.