weld tutorial @alternatives qualifierIn this tutorial we will illustrate how to use CDI to choose between different EJB implementation at deployment time, or at injection point.

 

 

 

CDI stands for Context Dependency Injection and can be used to for injecting Context and Dependencies into your application. This concept is not at all new for Java developers. Spring developers for example use dependencies by naming beans and binding them to their injection points by their names.
Although Spring is the major player in this field, what makes particularly attractive CDI is that it's part of the Java EE 6 stack, thus any container which is Java EE 6 compatible can run an application using CDI.

If you want to have an introduction to CDI you can look at this former tutorial.

In this one, we will show how you can choose between two different EJB implementations using CDI. Supposing you have a Java EE 6 application which has got two different EJB to handle a cache of keys/values.

    
package com.packtpub.chapter4.ejb;
 
import java.util.List;
import com.packtpub.chapter4.entity.Property;

public interface SingletonBean {
 
 public void initCache();

 public void delete();
 
 public void put(String key,String value);
 
 public List<Property> getCache();
 
}

And the two different EJB implementation classes:

This is SingletonBeanDB which persists the cache on the database (although cache is queried only once at startup. Subsequent calls use an in-memory List. Yet this optimisazion is just a detail for our example):
    
package com.packtpub.chapter4.ejb;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import com.packtpub.chapter4.entity.Property;

@Singleton
public class SingletonBeanDB implements SingletonBean {

 private List<Property> cache;
 @PersistenceContext(unitName = "persistenceUnit")
 private EntityManager em;

 @PostConstruct
 public void initCache() {
   System.out.println("Inited DB Cache");
   this.cache = queryCache();
   if (cache == null) cache = new ArrayList<Property>();
 }

 public void delete() {
   Query query = em.createQuery("delete FROM com.packtpub.chapter4.entity.Property");
   query.executeUpdate();
    this.cache.clear();
 }

 public void put(String key, String value) {
   Property p = new Property();
   p.setKey(key);
   p.setValue(value);
   em.persist(p);

   this.cache.add(p);
 }

 public List<Property> getCache() {
   return cache;
 }

 private List<Property> queryCache() {
   Query query = em.createQuery("FROM com.packtpub.chapter4.entity.Property");

   List<Property> list = query.getResultList();
   return list;
 }

}

And this is SingletonBeanMemory which stores in memory the cache of keys-values:
    
package com.packtpub.chapter4.ejb;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import com.packtpub.chapter4.entity.Property;


@Singleton
public class SingletonBeanMemory implements SingletonBean   {

 private  List<Property> cache;
 @PersistenceContext(unitName = "persistenceUnit")
 private EntityManager em;
 
 @PostConstruct
 public void initCache(){
   System.out.println("Inited Memory Cache");
   cache = new ArrayList<Property>();
 }

 public void delete(){
   this.cache.clear();
 }
 public void put(String key,String value){
   Property p = new Property();
   p.setKey(key);
   p.setValue(value);

   this.cache.add(p);
 }
 public List<Property> getCache() {
   return cache;
 }

}


Have you noticed the com.packtpub.chapter4 package ? as a matter of fact this example extends one other example found in JBoss AS 7 book at chapter 4

The Property class is a basic Entity which will be used to map a corresponding table on the DB:
    
package com.packtpub.chapter4.entity;

import javax.persistence.*;

@Entity
public class Property {

 @Id( )
 @Column(name="id")
 private String key;
 
 @Column(name="value")
 private String value;

 // getter/setter omitted for brevity

}


And this is the JSF Bean which wires the front-end to the EJBs. As you can see there is a tight dependency with one EJB in the init() method of the Bean:
    
package com.packtpub.chapter4.bean;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.annotation.PostConstruct;

import javax.enterprise.context.SessionScoped;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;

import javax.inject.Named;

import com.packtpub.chapter4.ejb.SingletonBean;
import com.packtpub.chapter4.ejb.SingletonBeanDB;

@Named(value="manager")
@SessionScoped
public class PropertyManager implements Serializable {

 SingletonBean ejb;
 
 @PostConstruct
 public void init() {
   // Tight dependency with the SingletonBeanDB
   ejb = new SingletonBeanDB();
 }
 ArrayList  cacheList  = new ArrayList ();

 private String key;
 private String value;

 // Getters and Setters omitted for brevity
 
 public void save(ActionEvent e) {
   ejb.put(key, value);
 }

 public void clear(ActionEvent e) {
   ejb.delete();
 }
 public List getCacheList() {
   return ejb.getCache();
 }


 public void get(ActionEvent e) {
   String msg = "Added key = " + key;
   FacesContext.getCurrentInstance().addMessage(null,
       new FacesMessage(FacesMessage.SEVERITY_WARN, msg, null));

 }
}


 


Ok so now how can we use CDI to solve the tight dependency with the EJBs ?

First approacht: using Qualifiers to solve the dependency at injection point.
The CDI specification (JSR-299) defines "Qualifer" as a means to uniquely identify one of the multiple implementations of the bean type to be injected. The spec defines certain built-in qualifiers (@Default, @Any, @Named, @New) and new qualifiers can be easily defined as well.

In this example we will define the Qualifier "MyCache" which will be used to select the bean implementation an injection point:

    
package com.packtpub.chapter4.qualifier;

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.inject.Qualifier;

@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface MyCache {
}


Now all we need to do, is selecting the EJB implementation by adding the @MyCache annotation on the top of the EJB. For example, supposing we want to switch to the SingletonBeanMemory:
    
@Singleton
@MyCache
public class SingletonBeanMemory implements SingletonBean   {
 . . . . .
}

On the other hand, this dependency need to be injected in the PropertyManager Bean used by JSF:
    
public class PropertyManager implements Serializable {

 @Inject @MyCache
 SingletonBean ejb;
 @PostConstruct
 public void init() {
   // Removed tight dependency here
 }

That's all. The following picture resumes the Class diagram for this use case:


cdi jboss weld tutorial qualifier

 



Second approach: using Alternatives to solve the dependency at deployment point.   
With @Alternative annotation you can package multiple beans that have the same injection point without errors -- you can apply the @Alternative annotation to more than one bean and specify the bean you want to use in the CDI beans.xml configuration file.

So we need flagging both EJBs with the @Alternatives annotation:

    
@Alternative
public class SingletonBeanDB implements SingletonBean { .. }

    
@Alternative 
public class SingletonBeanMemory implements SingletonBean   { .. }

then, in the beans.xml file we will specify which implementation we will use for our EJBs. For example, supposing we want to use the SingletonBeanDB
    
 <alternatives>
   <class>com.packtpub.chapter4.ejb.SingletonBeanDB</class>
 </alternatives>


Within the PropertyManager we will just use a generic @Injection which will be resolved at deployment time by CDI:
    
public class PropertyManager implements Serializable {

 @Inject  
 SingletonBean ejb;  
 ......
}

The following class diagram resumes our class schema:

cdi using @Alternatives annotation

You can download the code here which includes also the JSF view for adding data to the cache.

 

 

You can run this example application on JBoss AS 6 / 7. The application server auto-detects that you are using CDI, once a WEB-INF/beans.xml file is found in the application.

0
0
0
s2smodern