Advanced CDI

In this section, we will advance the context and dependency injection to listening and acting on the lifecycle events, and choose between alternative implementations through configuration.

The lifecycle component example

In the section, CDI initialization and destruction, we discussed the lifecycle methods for the CDI managed beans. Let us look at a unit test example that demonstrates the concepts. We will build the other side of the CreditProcessor example, which is the premium rate version.

package je7hb.standalone;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Premium
public class PremiumCreditProcessor implements CreditProcessor {
  @Override
  public void check(String account) {
    if (!account.trim().startsWith("1234")) {
      throw new RuntimeException("account:["+account+"] is not valid!");
    }
  }
  
  @PostConstruct
  public void acquireResource() {
    System.out.println( this.getClass() .getSimpleName()+"#acquireResource()");
  }
  
  @PreDestroy
  public void releaseResource() {
    System.out.println( this.getClass() .getSimpleName()+"#releaseResource()");
  }
}

The class PremiumCreditProcessor is a CDI managed bean, which is associated with the @Premium qualifier. It has two lifecycle methods acquireResource() and releaseResource().

The CDI container invokes @PostConstruct annotated methods after the bean type has been constructed. By the time the @PostConstruct method is called the CDI container has already initialized any instance field properties in the bean type and the super classes, which are managed by the CDI container. This lifecycle method is designed for all managed beans to initialize the additional resources, such as lazy-loadable and/or expensive resources, which need to be allocated.

The CDI container on managed beans invokes the @PreDestroy method, when the context with which they are associated is about to be destroyed. This lifecycle method is designed for the CDI managed beans to release any resources and/or de-allocate expensive resources, for example, freeing up data handles or memory.

Let us look at the following unit test:

package je7hb.standalone;
import je7hb.travelfunk.AbstractCdiContainerTest;
import org.junit.Test;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;

import static org.junit.Assert.assertNotNull;

public class ExpensiveCreditProcessorTest extends AbstractCdiContainerTest {
  @Inject @Premium @RequestScoped
  private CreditProcessor agent;
  
  @Test
  public void shouldInjectExpensiveCredit() {
    assertNotNull(agent);
    agent.check("12345678");
    System.out.printf("agent=%s
", agent);
  }
}

For the first time, in this unit test, we are making use of a specific scope, the request-scope, which is designed for the web servlet containers. Essentially, the request-scope is a collection of short-lived managed objects in a map collection. The request scope only exists for the duration of a single incoming HTTP web request that reaches the application server. As soon as the web request is consumed and a response is sent back to the client by a Java Servlet or something else, the objects in the Request scope should be garbage collected because the CDI container (and the web container) will destroy it.

The trick to the unit test lies within super class, the AbstractCdiContainerTest, specifically the tearDown() method where the Request scope is stopped and then restarted order to ensure the current instance is destroyed. Obviously, running in Weld Standalone SE and not inside an application server makes this code interesting.

Let us now turn our attention to getting configuration of beans when we want to deliver a special type of instance to a client, when we have a choice of alternatives.

Alternatives

CDI supports the concepts of alternatives, for situations where you have more than one implementation of an interface or plain object class. For example, you could have more than one type of food processor, which may be supplied by third parties and might be outside of our control. Your application may have a requirement that dictates only a certain processor that can be active at any time in the program.

Let us illustrate alternatives with an interface called FoodProcessor, which simply communicates the product's brand to the caller.

// FoodProcessor.java
public interface FoodProcessor {
    public String sayBrand();
}

Now let us define the implementations of our suppliers with just two classes like so:

// NouveauFoodProcessor.java
public class NouveauFoodProcessor implements FoodProcessor {
    @Override public String sayBrand() {
        return "Nouveau";
    }
}

// XenoniqueFoodProcessor.java
import javax.enterprise.inject.Alternative;

@Alternative
public class XenoniqueFoodProcessor implements FoodProcessor {
    @Override public String sayBrand() {
        return "Xenonique";
    }
}

In one of those brands of food processor, XenoniqueFoodProcessor, we designate at least one to be an alternative with the @Alternative annotation. There must be one implementation that is the default, which in this case is the class called NouveauFoodProcessor. How does the CDI container know which instance to plug into the application?

By default, if there is no configuration, then the CDI container will inject the NouveauFoodProcessor in to the type that requires it. If we want to specify the alternative, then we need to configure that in an XML configuration file, which is the bean container file META-INF/beans.xml the content looks like as follows:

<?xml version="1.0" encoding="UTF-8"?>
<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_1.xsd">
   <alternatives>
     <class>je7hb.standalone.alternatives
      .XenoniqueFoodProcessor</class>
   </alternatives>
</beans>

The alternative class is specified in the absolute XPath of /beans/alternative/class. The text of this element is the fully qualified name of the concrete class.

Alternatives can be useful for injecting a separate test service. Perhaps it could be a mock object or a prototype implementation that another developer team in the investment bank will fulfill at some later stage in the grand development phase.

So let us provide, now, a customary unit test to prove this fact:

import javax.inject.Inject;
public class AlternativesFoodProcessorTest 
extends AbstractCdiContainerTest {
    private @Inject FoodProcessor foodProcessor;

    @Test
    public void shouldInjectAlternative() {
        assertNotNull(foodProcessor);
        assertEquals("Xenonique",
         foodProcessor.sayBrand());
    }
}

The unit test is based on our DeltaSpike standalone abstract container. We simply instantiate the FoodProcessor type and verify that the instance is, indeed, a XenoniqueFoodProcessor, which of course it is.

Perhaps, you will have noticed that even though the DeltaSpike container is a step up from the standalone JBoss Weld SE examples; in fact it gives more portability to another CDI Container, namely Apache Open Web Beans; however it is still not enough for wide portability.

There are many more Java EE 6 products and, since the formal release, Java EE 7 products on the market that are fully certified and standardized implementations of the specification, and some are pending reaching that condition. Although DeltaSpike is a great solution, it does not cover all of them. We need something that is a testing framework, makes use of the CDI scope exactly the same as inside an application server and fully reproduces the conversational scope. This framework is called Arquillian and now we will study the essentials in the next section.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.150.231