Squeezing the Most Out of the Ajax Control Toolkit's Accordion Tool | 3 | WebReference

Squeezing the Most Out of the Ajax Control Toolkit's Accordion Tool | 3

By Joydip Kanjilal


[next]

Everything you knew about Java beans was redefined by the CDI (Contexts and Dependency Injection) concept. At the abstract level, a CDI bean is not like a common Java bean; it is more like a "source of contextual objects that define application state and/or logic". However, at the same time, a CDI bean is like any Java bean, because it unifies the Java beans under the same roof. At the container level, a CDI bean is a Java EE component that has a container controllable lifecycle, according to the lifecycle context model defined in the CDI specification.

At the development level, a CDI bean contains an entire set of provisions:

  • A CDI scope (it can be any of the following: request (@RequestScoped), session (@SessionScoped), application (@ApplicationScoped), dependent (@Dependent), conversation (@ConversationScoped)
  • A set of bean types -- empty not allowed
  • A set of qualifiers (used for various implementations of a particular bean type -- defined as @Target({METHOD, FIELD, PARAMETER, TYPE}) and @Retention(RUNTIME)) -- empty not allowed
  • A set of interceptor bindings
  • A bean implementation
  • A bean EL name (make a bean accessible through the EL using the @Named built-in qualifier) -- optional

Starting with Java EE 6, CDI becomes an integral part of and a powerful facility for Java EE components. Employing CDI, you can develop Java EE components that exist within the lifecycle of an application with well-defined scopes. In addition, CDI allows Java EE components such as JavaServer Faces (JSF) managed beans to be injected and makes it easy for developers to use enterprise beans along with JSF technology in Web applications. CDI is specified by JSR 299, also known as Web Beans, and the related specifications that CDI uses include Dependency Injection for Java (JSR 330) and Managed Beans (JSR 316).

In this article, you will see how to set up lifecycle-specific actions for CDI managed beans. The applications developed in this article need NetBeans 6.9 (or 6.8 with CDI patch), JDK 6, and GlassFish v3. Notice that NetBeans provides powerful support for CDI; therefore, it is worth exploring its support, instead of hand coding or using third-party tools.

However, before that, let us have a short overview of the CDI managed bean concept.

Overview of CDI Managed Beans

A top-level Java class is a managed bean if it is defined to be a managed bean by any other Java EE technology specification (like JSF), or if it meets the below set of conditions:

  • It is not a nonstatic inner class.
  • It is a concrete class or is annotated @Decorator.
  • It is not annotated with an EJB component-defining annotation or declared as an EJB bean class in ejb-jar.xml.
  • It has an appropriate constructor. That is, one of the following is the case:
    • The class has a constructor with no parameters.
    • The class declares a constructor annotated @Inject.

Note: No special declaration, such as an annotation, is required to define a managed bean.

Lifecycle-specific Actions for a CDI Managed Bean

CDI managed beans support the PostConstruct and PreDestroy lifecycle callbacks.

  • The PostConstruct lifecycle callback is represented by the @PostConstruct annotation. This annotation is used on a method that needs to be executed after dependency injection is done to perform any initialization. The @PostConstruct annotated method will be called by the container after the bean has been constructed and before any business methods of the bean are executed.
  • The PreDestroy lifecycle callback is represented by the @PreDestroy annotation. This annotation is used on methods to signal that the instance is in the process of being removed by the container. A @PreDestroy annotated method will execute directly after the @Remove method has finished executing and before the bean instance is removed by the container. After this method has executed the bean can be garbage collected.

Before applying these annotations, you need a CDI managed bean. You can develop a simple CDI managed bean, like below -- your bean is in RequestScope and has a String property, named ballType, and an action method, named generateRandomBall. The generateRandomBall generates an int and stores a random ball type (a random text) in the ballType property:

package com.games.balls;
import java.util.Random;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
@Named
@RequestScoped
public class BallBean {
    private String ballType;
    private Random generator = new Random();
   public void generateRandomBall() {     
        int randomValue = generator.nextInt(3);
        switch (randomValue) {
            case 1:
                this.ballType = "Tennis Ball";
                System.out.println("*** Tennis Ball Generated ***");
                break;
            case 2:
                this.ballType = "Volley Ball";
                System.out.println("*** Volley Ball Generated ***");
                break;
            case 3:
                this.ballType = "Football Ball";
                System.out.println("*** Football Ball Generated ***");
                break;
            default:
                this.ballType = "No Ball";
                System.out.println("*** No Ball Generated ***");
        }
    }
     public String getBallType() {
        return ballType;
    }
    public void setBallType(String ballType) {
        this.ballType = ballType;
    }
}

The method generateRandomBall is called from the Facelets code listed below:

<?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>Get a random ball</title>
    </h:head>
    <h:body>
        <h:form id="myBall">
            <p><h:outputLabel value="Press the button to get a random ball ..."/></p>
            <p><h:commandButton value="Get ball" action="#{ballBean.generateRandomBall}"/></p>
            <p><h:outputText value="#{ballBean.ballType}"/></p>
        </h:form>
    </h:body>
</html>

If you test the application at this moment, then in the GlassFish logs you will find an output like below:

[#|2011-01-10T06:06:42.693-0800|INFO|glassfish3.0.1|javax.enterprise.system.std.com.sun.enterprise.v3.services.impl|_ThreadID=26;_ThreadName=Thread-1;|*** No Ball Generated ***|#]
[#|2011-01-10T06:06:44.704-0800|INFO|glassfish3.0.1|javax.enterprise.system.std.com.sun.enterprise.v3.services.impl|_ThreadID=27;_ThreadName=Thread-1;|*** No Ball Generated ***|#]
[#|2011-01-10T06:06:45.757-0800|INFO|glassfish3.0.1|javax.enterprise.system.std.com.sun.enterprise.v3.services.impl|_ThreadID=26;_ThreadName=Thread-1;|*** Tennis Ball Generated ***|#]
[#|2011-01-10T06:06:46.535-0800|INFO|glassfish3.0.1|javax.enterprise.system.std.com.sun.enterprise.v3.services.impl|_ThreadID=27;_ThreadName=Thread-1;|*** Volley Ball Generated ***|#]
[#|2011-01-10T06:06:47.248-0800|INFO|glassfish3.0.1|javax.enterprise.system.std.com.sun.enterprise.v3.services.impl|_ThreadID=26;_ThreadName=Thread-1;|*** Tennis Ball Generated ***|#]

If you think of the random generator as a resource, then it will be elegant to have a @PostConstruct annotated method for creating it and a @PreDestroy annotated method for destroying it. Since the @PostConstruct annotated method is called before any other business logic method is called (in your case the generateRandomBall method) the generator will be available just on time. In addition, before the bean is destroyed the generator can be also destroyed into a @PreDestroy annotated method. You can modify the above bean, like this:

package com.games.balls;
import java.util.Random;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
@Named
@RequestScoped
public class BallBean {
    private String ballType;
    private Random generator;
    @PostConstruct
    private void startGenerator(){
        System.out.println("*** RANDOM GENERATOR CREATED ***");
        generator = new Random();
    }
    
    @PreDestroy
    private void stopGenerator(){
        System.out.println("*** RANDOM GENERATOR DESTROYED ***");
        generator = null;
    }
    public void generateRandomBall() {        
        int randomValue = generator.nextInt(3);
        switch (randomValue) {
            case 1:
                this.ballType = "Tennis Ball";
                System.out.println("*** Tennis Ball Generated ***");
                break;
            case 2:
                this.ballType = "Volley Ball";
                System.out.println("*** Volley Ball Generated ***");
                break;
            case 3:
                this.ballType = "Football Ball";
                System.out.println("*** Football Ball Generated ***");
                break;
            default:
                this.ballType = "No Ball";
                System.out.println("*** No Ball Generated ***");
        }
    }
     public String getBallType() {
        return ballType;
    }
    public void setBallType(String ballType) {
        this.ballType = ballType;
    }
}

If you test the application at this moment, then in the GlassFish logs you will find an output like below -- notice the flag messages that marks the @PostConstruct and @PreDestroy effects. Since the bean is in RequestScope, the generator is created and destroyed after each HTTP request is accomplished:

[#|2011-01-10T06:18:09.487-0800|INFO|glassfish3.0.1|javax.enterprise.system.std.com.sun.enterprise.v3.services.impl|_ThreadID=26;_ThreadName=Thread-1;|*** RANDOM GENERATOR CREATED ***|#]
[#|2011-01-10T06:18:09.491-0800|INFO|glassfish3.0.1|javax.enterprise.system.std.com.sun.enterprise.v3.services.impl|_ThreadID=26;_ThreadName=Thread-1;|*** RANDOM GENERATOR DESTROYED ***|#]
[#|2011-01-10T06:18:11.664-0800|INFO|glassfish3.0.1|javax.enterprise.system.std.com.sun.enterprise.v3.services.impl|_ThreadID=26;_ThreadName=Thread-1;|*** RANDOM GENERATOR CREATED ***|#]
[#|2011-01-10T06:18:11.664-0800|INFO|glassfish3.0.1|javax.enterprise.system.std.com.sun.enterprise.v3.services.impl|_ThreadID=26;_ThreadName=Thread-1;|*** No Ball Generated ***|#]
[#|2011-01-10T06:18:11.668-0800|INFO|glassfish3.0.1|javax.enterprise.system.std.com.sun.enterprise.v3.services.impl|_ThreadID=26;_ThreadName=Thread-1;|*** RANDOM GENERATOR DESTROYED ***|#]
[#|2011-01-10T06:18:12.400-0800|INFO|glassfish3.0.1|javax.enterprise.system.std.com.sun.enterprise.v3.services.impl|_ThreadID=26;_ThreadName=Thread-1;|*** RANDOM GENERATOR CREATED ***|#]
[#|2011-01-10T06:18:12.400-0800|INFO|glassfish3.0.1|javax.enterprise.system.std.com.sun.enterprise.v3.services.impl|_ThreadID=26;_ThreadName=Thread-1;|*** Tennis Ball Generated ***|#]
[#|2011-01-10T06:18:12.408-0800|INFO|glassfish3.0.1|javax.enterprise.system.std.com.sun.enterprise.v3.services.impl|_ThreadID=26;_ThreadName=Thread-1;|*** RANDOM GENERATOR DESTROYED ***|#]

If you modify the bean scope as SessionScope -- do not forget to implement the Serializable interface for passivation tasks -- then the generator will be created when the session starts, and will be destroyed when the session is destroyed.


[next]