The Hollywood Principle

Tutorial - Introduction to CDI - Contexts and Dependency Injection for Java EE (JSR 299) - Part 4

 

Producer Methods

In the previous example, we knowingly omitted how those EntityManagers will be created. One possibility would be to use Producer Methods:

Listing 3

public class MyEntityManagerProducers {
 @Produces @RequestScoped @CustomerDb
 public EntityManager createCustomerDbEm() {
   return Persistence.createEntityManagerFactory(„customerDb“).
          createEntityManager();
 }

 @Produces @RequestScoped @AdminDb
 public EntityManager createAdminEm() {
   return Persistence.createEntityManagerFactory("adminDb").
          createEntityManager();
 }
 ...

We create @RequestScoped EntityManagers, since an EntityManager is per definition not serialisable – thus we cannot store it in the session. We also need to implement a way to properly clean up the EntityManagers at the end of each request. This can be done with disposal methods, using the @Disposes annotation:

 // class MyEntityManagerProducers continued
 public void disposeUdEm(@Disposes @UserData EntityManager em) {
   em.close();
 }
 public void disposeBoEm(@Disposes @BackOffice EntityManager em) {
   em.close();
 }
}

Events

The CDI specification defines a flexible but very easily usable eventing mechanism based on the Observer/Observable pattern.

In many EE applications it makes sense to ‘cache’ some information in the session. An example of such information would be user roles and privileges and the menu tree based on those rights. It's usually not necessary to perform this expensive calculation for each and every request. Rather, it can simply be stored in the session.

One problem with this approach is that changing user settings during runtime – e.g. when a user logs in temporarily as administrator or changes his view language – is not easy. By using the CDI event system we can implement this in a very elegant way. Instead of manually cleaning up all depending information, we just send a UserSettingsChanged event – and everyone who is interested can react accordingly. The event itself is typesafely represented by a class, which might also contain payload data:

public class UserSettingsChanged {
 public UserSettingsChanged(String userName) {
   this.userName = userName;
 }
 private String userName; // + getter und setter
 ...
}

Let's now focus on the event source. For firing a UserSettingsChangedEvent, we first need to inject an event-source:

public class MyLoginBean {
 private @Inject Event<UserSettingsChanged> userChangedEvent;
 ...
 public boolean login(String username, String password) {
   .. do the login stuff
   userChangedEvent.fire(new UserSettingsChanged(username));
   ...
 }
} 

Any class which needs to react on this event can now comfortably observe it via an observer method:

public @SessionScoped class MyBackingBean {
 Locale userLanguage;
 ...
 public void refreshLanguage(@Observes UserSettingsChanged usc) {
   userLanguage = getDefaultLanguageOfUser(usc.getUserName());
 }
 ...
}
If the UserSettingsChange event gets fired, all observer methods of beans in currently active scopes will get invoked.

Inceptors

CDI provides an easy way to create own custom interceptors, as we will show by creating our own @Transactional interceptor. Instead of having to manage the transactions manually, our small interceptor will do this for us as shown in the following usage example:

@ApplicationScoped
public class MyUserService {
 private @Inject EntityManager em;

 @Transactional
 public storeUser(User u) {
   em.persist(u);
 }
}

For implementing this feature, we need to provide two parts. The first one is obviously the annotation itself. It is meta-annotated as @InterceptorBinding which marks it as an annotation that is intended to be used for interceptors:

@Retention(RetentionPolicy.RUNTIME)
@Target( { ElementType.TYPE, ElementType.METHOD })
@InterceptorBinding
public @interface Transactional {}

The second part is the interceptor implementation itself. This class must be annotated as @Interceptor and additionally with its intended interceptor binding. The interceptor functionality itself is implemented in a method which gets annotated as @AroundInvoke:

Listing 4

@Interceptor @Transactional
public class TransactionalInterceptor {
 private @Inject EntityManager em;
 @AroundInvoke
 public Object invoke(InvocationContext context) throws Exception{
   EntityTransaction t =em.getTransaction();
   try {
     if(!t.isActive()) 
       t.begin();    
     return context.proceed();
   } catch(Exception e) {
     .. rollback and stuff
   } finally {
     if(t != null && t.isActive()) 
       t.commit();
   }
 } 
}

Final Thoughts

After two years of availability, CDI is already seeing wide adoption. It has proven itself in a wide range of projects, from providing productivity in small start-ups to offering reliability and scalability websites with millions of users per day.

The Expert Group is currently working actively on CDI 1.1, a specification which will bring small fixes and improvements, including much-requested standardization of Java SE bootstrap functionality for non-web applications.

 

More about the Authors:

Mark Struberg is a software architect with over 20 years of programming experience. He has been working with Java since 1996 and is actively involved in open source projects in the Java and Linux area. He is Apache Software Foundation member and serves as PMC and Committer for Apache OpenWebBeans, MyFaces, Maven, OpenJPA, BVal, DeltaSpike and other projects. He is also a CDI Expert Group member actively working on the specification. Mark works for the Research Group for Industrial Software (INSO) at the Vienna University of Technology.

Pete leads the Seam, Weld and CDI TCK projects, is an adviser to the RichFaces project, and is a founder of the Arquillian project. He has worked on a number of specifications including JSF 2.0, AtInject and CDI. He is a regular speaker at JUGs and conferences such as Devoxx (Javapolis), JAX, JavaBlend, JSFDays and JBoss World. Pete is currently employed by Red Hat Inc. working on JBoss open source projects. Before working for Red Hat, he used and contributed to Seam whilst working at a UK based staffing agency as IT Development Manager.

 

This article originally appears in Java Tech Journal: CDI back in March 2012 - find more of that issue (and others) here:

Mark Struberg
Mark Struberg
Peter Muir
Peter Muir

What do you think?

JAX Magazine - 2014 - 06 Exclucively for iPad users JAX Magazine on Android

Comments

Latest opinions