In part 1, we looked at creating a JEE 6 application with Netbeans using JSF and CDI running on Glassfish. Now we’ll take a closer look at using CDI for managing dependencies in a Java EE 6 environment.

Read Part 1
Read Part 2
Read Part 3

Last Time

Last time we looked at setting up the development environment and creating our EE6 application in Netbeans and deploying it to Glassfish v3. This time, we’ll skip straight to writing code which can be put into either a new Netbeans project using the previous tutorial, or you can use the existing project and just add the new code.

The ‘I’ in CDI

CDI is an API for injecting contexts and dependencies which is the part we’ll turn our attention to now. In Seam and Spring dependencies worked mostly by naming beans and binding them to their injection points by their name. So far we have only referenced a managed bean by name from the JSF page when we defined the name for the bean using the @Named annotation. The primary role of the @Named annotation is to define the bean for the purpose of resolving EL statements within the application, usually through the JSF EL resolvers. Injection could be performed by using names, but this was not how injection in CDI was meant to work since CDI gives us a much richer way to express injection points and the beans to be injected into them.

Let’s look at an example which is somewhat contrived. We have a dao that returns a list of objects that need validating, and for invalid ones, we take a certain action. Here’s the definition for the item.

package eedemo;

public class Item {

    private int value;
    private int limit;

    @Override
    public String toString() {
        return super.toString() + String.format(" [Value=%d, Limit=%d]", value,limit);
    }

    /*
    getters and setters omitted
    */
}

The ItemDao interface defines how we get the list of item objects. In this test application we anticipate using multiple implementations so we will code to interfaces.

public interface ItemDao {

    List<Item> fetchItems();

}

The ItemProcessor is our main class that we will inject our beans into and execute the process from. For now, we will start with the Dao and look at how we will inject it into our processor bean.

import java.util.List;
import javax.inject.Named;
import javax.enterprise.context.RequestScoped;

@Named("itemProcessor")
@RequestScoped
public class ItemProcessor {

    private ItemDao itemDao;

    public void execute() {
      List<Item>  items = itemDao.fetchItems();
      for (Item item : items) {
          System.out.println("Found item "+item);
      }
    }
}

We’ll start with a simple Dao that just creates a list of items and returns a fixed list of items.

public class DefaultItemDao implements ItemDao {

    public List<Item> fetchItems() {
        List<Item> results = new ArrayList<Item>();
        results.add(new Item(34, 7));
        results.add(new Item(4, 37));
        results.add(new Item(24, 19));
        results.add(new Item(89, 32));
        return results;
    }
}

In order to inject the DefaultItemDao into our ItemProcessor we add the javax.inject.Inject annotation to the ItemDao field to indicate that this field is an injection point.

@Named("itemProcessor")
@RequestScoped
public class ItemProcessor {

    @Inject
    private ItemDao itemDao;

    ...
}

Finally, we need some way to call the execute()ItemProcessor. We can run this in a SE environment, but for now we’ll keep it in a JSF page. We’ll add a new page with a button to call the execute method called process.xhtml.

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
        <title>Item Processor</title>
    </h:head>
    <h:body>
        <h:form>
        <h:commandButton action="#{itemProcessor.execute}" value="Execute"/><br/>
        </h:form>
    </h:body>
</html>

If you open up the page at http://localhost:8080/ee6demo/faces/process.xhtml you will see just a button that when clicked lists the items from our default Dao implementation in the console.

INFO: Found item eedemo.Item@23cbc6 [Value=34, Limit=7]
INFO: Found item eedemo.Item@a40279 [Value=4, Limit=37]
INFO: Found item eedemo.Item@19e6292 [Value=24, Limit=19]
INFO: Found item eedemo.Item@1597bc4 [Value=89, Limit=32]

We created a class which implements the ItemDao interface and when the application was deployed our managed beans in the module were processed by the CDI implementation (again because of the beans.xml file in the module. Our Inject annotation specifies that we want to inject a managed bean into that field and the only thing we know about the bean to inject is that it must implement ItemDao or some subtype of that interface. In this case, the DefaultItemDao class fits the bill perfectly.

Let’s say we add another Dao class to our application which also implements the ItemDao interface, now the choice isn’t so clear as to which bean we want to inject.

public class AnotherItemDao implements ItemDao {

    public List<Item> fetchItems() {
        List<Item> results = new ArrayList<Item>();
        results.add(new Item(99, 9));
        return results;
    }

}

We now have two classes the implement this interface and predictably, Weld gives us an ambiguous dependency error meaning that it cannot determine what bean to use for that injection point. Most, if not all of the errors that can occur with regards to CDI injection in Weld are reported at deployment time, even down to whether beans are missing a Serializable implementation.

Caused by: org.jboss.weld.DeploymentException: Injection point has ambiguous dependencies. 
Injection point: field eedemo.ItemProcessor.itemDao; Qualifiers: [@javax.enterprise.inject.Default()]; 

Possible dependencies: [eedemo.AnotherItemDao, eedemo.DefaultItemDao]

We could make our itemDao field in the item processor a type that matches one of the implementation types (AnotherItemDao or DefaultItemDao) since it would then match one and only one class type. However, then we would lose the benefits of coding to an interface and find it harder to change implementations without changing the field type. A better solution is to instead look at CDI Qualifiers.

Qualifiers

A CDI qualifier is an annotation that can be applied at the class level to indicate the kind of bean the class is, and also at the field level (among other places) to indicate what kind of bean needs to be injected at that point.

When CDI inspects an injection point to find a suitable bean to inject it takes not only the class type into account, but also any qualifiers. Without knowing it, we have already used one qualifier which is the default qualifier called @Any. Let’s create a @Demo qualifier which we can apply to our DefaultItemDao implementation and also to the injection point.

package eedemo.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 java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.inject.Qualifier;

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

}

First, we’ll add this qualifier to our default dao implementation at the class level.

@Demo
public class DefaultItemDao implements SomeDao {

    public List<Item> fetchItems() {
..
..

If you save and deploy this file now, you may notice that we don’t have any errors, and if you go to the web page and click the execute button, you can see that in the console, it displays the list of items from the AnotherItemDao dao (remember we annotated the DefaultItemDao implementation but not the injection point). By adding the Demo qualifier to the default dao implementation, we made the other implementation a more suitable match for the injection point as it matched on type and on qualifiers and the DefaultItemDao has a qualifier that is not on the injection point making it less suitable.

If we add the Demo annotation to the injection point and deploy it, when we click our button we use the default implementation again. This is because we are matching based on type and qualifiers and the DefaultItemDao is the only bean with both the correct type and the Demo annotation.

Alternative Injection Methods

There are multiple ways to define an injection point on the injected class. So far we have annotated the fields that will reference the injected object. You do not need to provide getters and setters for field injection. If we wish to create immutable managed beans with final fields, we can use injection in the constructor by annotating the constructor with the Inject annotation. We can then apply any annotations to constructor parameters to qualify beans for injection (of course, each parameter has a type that can assist in qualifying beans for injection). A bean may only have one constructor with injection points defined, but it may implement more than one constructor.

@Named("itemProcessor")
@RequestScoped
public class ItemProcessor {

    private final ItemDao itemDao;

    @Inject
    public ItemProcessor(@Demo ItemDao itemDao) {
        this.itemDao = itemDao;
    }
}

We can also call an inialization method which can be passed the beans to be injected.

@Named("itemProcessor")
@RequestScoped
public class ItemProcessor {

    private ItemDao itemDao;

    @Inject
    public void setItemDao(@Demo ItemDao itemDao) {
        this.itemDao = itemDao;
    }
} 

While in the above case we used the setter method for initialization we can create any method and use it for initialization with as many beans as we want in the method call. We can also have multiple initialization methods in a bean.

    @Inject
    public void initBeans(@Demo ItemDao itemDao,@SomeQualifier SomeType someBean) {
        this.itemDao = itemDao;
        this.bean = someBean;
    }

The same rules apply to bean matching regardless of how the injection point is defined, it will try and find the best match based on type and qualifiers and will fail on deployment if there are multiple matching beans or no matching beans for an injection point.

Let’s look at the other aspects of our application, we get a list of items, iterate through them and test each one and if it fails that test, we pass it on to an error handler. We’ll create an ItemValidator interface to determines whether an item is valid or not.

public interface ItemValidator {
    boolean isValid(Item item);
}

We’ll expand our ItemProcessor class to incorporate the new feature.

@Named("itemProcessor")
@RequestScoped
public class ItemProcessor {

    @Inject @Demo
    private ItemDao itemDao;
    
    @Inject
    private ItemValidator itemValidator;

    public void execute() {
      List<Item>  items = itemDao.fetchItems();
      for (Item item : items) {          
          System.out.println("Item = "+item+" valid = "+itemValidator.isValid(item));
      }
    }
} 

Our first implementation will be DefaultItemValidator which will simply test the limit against the value.

public class DefaultItemValidator implements ItemValidator {

    public boolean isValid(Item item) {
        return item.getValue() < item.getLimit();
    }
}

If we save our changes, go to our web page and click our button, in the console you will see that our items are being validated and the only valid item is where the value is less than the limit.

INFO: Item = eedemo.Item@25dd89 [Value=34, Limit=7] valid = false
INFO: Item = eedemo.Item@1f37ca2 [Value=4, Limit=37] valid = true
INFO: Item = eedemo.Item@7b8d67 [Value=24, Limit=19] valid = false
INFO: Item = eedemo.Item@18075da [Value=89, Limit=32] valid = false

Now lets consider the scenario where we have a deployment to a different site that is more relaxed and considers an item invalid only if the value is more than twice the limit. We may want to have another bean that implements the validator interface for that logic.

public class RelaxedItemValidator implements ItemValidator {

    public boolean isValid(Item item) {
        return item.getValue() < (item.getLimit() * 2);
    }
}

Now we have an ambiguous dependency problem since we have two classes implementing the same interface. The only difference is based on deployment so for most deployments, we want to use the default, but for one deployment, we want to use the relaxed implementation. CDI offers the use of the Alternative annotation which lets you package multiple beans that match an injection point without ambiguity errors, and the bean to use is defined in the beans.xml. This means you can deploy both implementations in the same module with the only difference being the beans.xml definition which can change over different deployments.


@Alternative
public class DefaultItemValidator implements ItemValidator {

    public boolean isValid(Item item) {
        return item.getValue() < item.getLimit();
    }
}

and

@Alternative
public class RelaxedItemValidator implements ItemValidator {

    public boolean isValid(Item item) {
        return item.getValue() < (item.getLimit() * 2);
    }
}

If we deploy our application now, we will get an unsatisfied dependency error since we defined the two matching beans as alternative but we didn’t enable either of them in the beans.xml file.

<beans
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
    http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
    <alternatives>
        <class>eedemo.RelaxedItemValidator</class>
    </alternatives>
</beans> 

This tells CDI that for this deployment we want to use the RelaxedItemValidator. You can think of the alternative annotation as effectively disabling the bean making it unavailable for injection, but allowing the implementation to be packaged with the other beans. Adding it as an alternative in the beans.xml file effectively enables the bean making it available for injection. By moving this type of metadata to the beans.xml file, we can bundle different versions of the file with different deployments.

Handling Invalid Items

Continuing the example, invalid items are sent to the ItemErrorHandler as they are discovered.

public interface ItemErrorHandler {
    void handleItem(Item item);
}

Let’s start by implementing a fake handler that saves the item details to a file. We want to open the file before we start handling items, leave it open for the duration of the process as we add content to the file, and then close the file when we are done with our processing. We could manually add initProcess() and finishProcess() methods to the error reporter bean, but then we couldn’t code to the interface since the caller would need to know about those class specific methods. We could add those same methods to the ItemErrorReporter interface but then we would have to unnecessarily implement those methods in every class that implements that interface. Instead, we can use some of the lifecycle annotations from the Managed Bean spec to call methods on the bean at certain points in the bean lifecycle. A PostConstruct annotated method is called when the bean has been constructed and any dependencies the bean has have been injected. Likewise, a PreDestroy annotated method is called just before the bean is disposed of by the container.

public class FileErrorReporter implements ItemErrorHandler {

    @PostConstruct
    public void init() {
        System.out.println("Creating file error reporter");
    }

 @PreDestroy
    public void release() {
        System.out.println("Closing file error reporter");
    }

    public void handleItem(Item item) {
        System.out.println("Saving "+item+" to file");        
    }
}

Our final change is to add the item error handling into our ItemProcessor bean.

@Named("itemProcessor")
@RequestScoped
public class ItemProcessor {

    @Inject @Demo
    private ItemDao itemDao;
    
    @Inject
    private ItemValidator itemValidator;

    @Inject 
    private ItemErrorHandler itemErrorHandler;

    public void execute() {
      List<Item>  items = itemDao.fetchItems();
      for (Item item : items) {
          if (!itemValidator.isValid(item)) {
              itemErrorHandler.handleItem(item);
          }
      }
    } 
} 

When we run this from our browser we see the following in the console ;

INFO: Creating file error reporter
INFO: Saving eedemo.Item@d8b01e [Value=34, Limit=7] to file
INFO: Saving eedemo.Item@12a5e8 [Value=89, Limit=32] to file
INFO: Closing file error reporter

Next Time

Different application deployments might use different rules for handling invalid items. such as rejecting the item, to sending notifications to individuals or just flagging them or listing them in an output file. In addition, we may want to do a combination of these (reject an order, send email to the sales rep, and list it in a file).
One great way to handle this kind of multi-faceted problem is using events which we’ll look at next time.

Click to view getting started with jsf 2.0 and CDI part 3