Using CDI and Dependency Injection for Java in a JSF 2.0 Application

by Roger Kitain

This Tech Tip covers the intersection of three powerful technologies that are part of the Java EE 6 platform: JSR 299: Contexts and Dependency Injection, JSR 330: Dependency Injection For Java, and JSR 314: JavaServer Faces 2.0.

JSR 299: Contexts and Dependency Injection (CDI) defines a set of services for the Java EE environment that makes applications much easier to develop. It provides an architecture that allows Java EE components such as servlets, enterprise beans, and JavaBeans to exist within the lifecycle of an application with well-defined scopes. In addition, CDI services allow Java EE components, including EJB session beans and JavaServer Faces (JSF) managed beans to be injected and to interact in a loosely coupled way by firing and observing events. Perhaps most significantly, CDI unifies and simplifies the EJB and JSF programming models. It allows enterprise beans to act as managed beans in a JSF application. Through its services, CDI brings transactional support to the web tier. This can make it a lot easier to access transactional resources in web applications. For example, CDI services makes it a lot easier to build a Java EE web application that accesses a database with persistence provided by the Java Persistence API.

JSR 330: Dependency Injection For Java introduces a standard set of annotations that can be used for dependency injection. Dependency injection is a popular technique in developing enterprise Java applications. Unfortunately, there has not been a standard approach for annotation-based dependency injection. Dependency Injection For Java changes that by providing a standardized and extensible API for dependency injection. The API comprises a set of annotations for use on injectable classes.

JavaServer Faces technology provides a server-side component framework that is designed to simplify the development of user interfaces (UIs) for Java EE applications. The latest release of the technology, JSR 314: JavaServer Faces 2.0, makes UI development for Java EE applications even easier through support for annotations and the addition of new features such as Facelets and composite components.

This Tech Tip illustrates the use of CDI and Dependency Injection for Java in a JSF 2.0 application.

An Example Application

Let's look at some key parts of a JSF 2.0 application that uses CDI and Dependency Injection for Java. You can find the source code for the application in the sample application package that accompanies this tip. See Running the Sample Code for instructions on how to install and run the application.

Figure 1 shows the UI for the application. The UI prompts a user to guess a number that the system has randomly selected. The prompt is as follows: I am thinking of a number between min to max, where min and max represent the minimum and maximum values allowable as a guess, respectively. The UI displays a text field for the user to enter the number, a Guess button to submit the number, and a Reset button to restart the game. If the user enters a number that is lower than the correct number, the UI responds with the message Higher! It also changes the min value in the prompt message to one more than the guessed number. If the user's entry is too high, the UI responds with the message Lower! and changes the max value in the prompt message to one less than the guessed number. The system sets a limit for the number of guesses, and with each incorrect guess, the UI displays a message that tells the user how many guesses remain. The game ends when the user correctly guesses the number or when the user reaches the limit of guesses.

The UI for the Guess Number JSF 2.0 Application
Figure 1. The UI for the Guess Number JSF 2.0 Application
 

Here is the code for the application's UI:

   1.  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
   2.  <html xmlns="http://www.w3.org/1999/xhtml"
   3.     xmlns:ui="http://java.sun.com/jsf/facelets"
   4.     xmlns:h="http://java.sun.com/jsf/html"
   5.     xmlns:f="http://java.sun.com/jsf/core">
   6.     <h:head>
   7.         <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
   8.         <title>JSF 2.0 Weld Example</title>
   9.     </h:head>
  10.     <h:body>
  11.        <h:form id="NumberGuessMain">
  12.           <h:panelGrid styleClass="title-panel">
  13.              <h:outputText value="Guess Number" styleClass="title-panel-text"/>
  14.              <h:outputText value="Powered By JavaServer Faces 2.0 and Weld" styleClass="title-panel-subtext"/>
  15.           </h:panelGrid>
  16.           <div style="color: black; font-size: 24px;">
  17.              I'm thinking of a number between <span style="color: blue">#{game.smallest}</span> and <span style="color: blue">#{game.biggest}</span>. You have <span style="color: blue">#{game.remainingGuesses}</span>guesses.
  18.           </div>
  19.           <h:panelGrid border="1" columns="5" style="font-size: 18px;">
  20.              Number:
  21.              <h:inputText id="inputGuess" value="#{game.guess}" required="true" size="3" disabled="#{game.number eq game.guess}" validator="#{game.validateNumberRange}"/>
  22.              <h:commandButton id="GuessButton" value="Guess" action="#{game.check}" disabled="#{game.number eq game.guess}"/>
  23.              <h:commandButton id="RestartButton" value="Reset" action="#{game.reset}" immediate="true" />
  24.              <h:outputText id="Higher" value="Higher!" rendered="#{game.number gt game.guess and game.guess ne 0}" style="color: red"/>
  25.              <h:outputText id="Lower" value="Lower!" rendered="#{game.number lt game.guess and game.guess ne 0}" style="color: red"/>
  26.           </h:panelGrid>
  27.           <div style="color: red; font-size: 14px;">
  28.              <h:messages id="messages" globalOnly="false"/>
  29.           </div>
  30.           <h:outputStylesheet name="stylesheet.css" />
  31.        </h:form>
  32.     </h:body>
  33. </html>

The code for the UI should look familiar to you if you develop applications with JSF. In fact, everything on the page is standard JSF 2.0 view markup. Notice the highlighted expression language (EL) expressions. These expressions refer to a contextual bean instance named game. A contextual bean instance is also known as a managed bean or simply a bean.

Actually, the concept of managed beans goes beyond CDI. Managed beans, which is introduced in Java EE 6, is designed to unify all of the various types of beans in Java EE, including JSF managed beans, enterprise beans, and CDI beans. A managed bean is a Java class that is treated as a managed component by the Java EE container. Optionally, you can give it a name in the same namespace as that used by EJB components. A managed bean can also rely on a small number of container-provided services, mostly related to lifecycle management and resource injection. Other Java EE technologies such as JSF, EJB, and CDI build on this basic definition of a managed bean by adding services. So, for example, a JSF managed bean adds lifecycle scopes, an EJB session bean adds services such as support for transactions, and a CDI bean adds services such as dependency injection.

Returning to the highlighted EL expressions in the code for the UI, the EL expressions bind to various bean properties and methods, as follows:

  • The EL expressions in line 17, 24, and 25 bind to bean properties.
  • The EL expressions in line 21 bind to bean properties and to a bean validation method.
  • The EL expressions in lines 22 and 23 bind to bean action methods.

As you can see, in JSF 2.0, binding to a CDI bean is no different than binding to a typical JSF managed bean.

The Anatomy of a Simple Contextual Bean

As mentioned previously, beans can be bound to a lifecycle context, can be injected, and can interact with other beans in a loosely coupled way by firing and observing events. In addition, a bean may be called directly from Java code, or as you've seen in the UI for the example application, it may be invoked in an EL expression. This enables a JSF page to directly access a bean.

Let's examine the game bean used in the application. Here is its source code:

   1. package weldguess;
   2.
   3. import java.io.Serializable;
   4. import javax.annotation.PostConstruct;
   5. import javax.enterprise.context.SessionScoped;
   6. import javax.enterprise.inject.Instance;
   7. import javax.inject.Inject;
   8. import javax.inject.Named;
   9. import javax.faces.application.FacesMessage;
  10. import javax.faces.component.UIComponent;
  11. import javax.faces.component.UIInput;
  12. import javax.faces.context.FacesContext;
  13.
  14. @Named
  15. @SessionScoped
  16. public class Game implements Serializable {
  17.     private static final long serialVersionUID = 1L;
  18.
  19.     private int number;
  20.     private int guess;
  21.     private int smallest;
  22.
  23.     @MaxNumber @Inject
  24.     private int maxNumber;
  25.
  26.     private int biggest;
  27.     private int remainingGuesses;
  28.
  29.     @Random @Inject Instance<Integer> randomNumber;
  30.
  31.     public Game() {
  32.     }
  33.
  34.     public int getNumber() {
  35.          return number;
  36.     }
  37.
  38.     public int getGuess() {
  39.          return guess;
  40.     }
  41.
  42.     public void setGuess(int guess) {
  43.          this.guess = guess;
  44.     }
  45.
  46.     public int getSmallest() {
  47.          return smallest;
  48.     }
  49.
  50.     public int getBiggest() {
  51.         return biggest;
  52.     }
  53.
  54.     public int getRemainingGuesses() {
  55.         return remainingGuesses;
  56.     }
  57.
  58.     public String check() throws InterruptedException {
  59.         if (guess>number) {
  60.             biggest = guess - 1;
  61.         }
  62.         if (guess<number) {
  63.             smallest = guess + 1;
  64.        }
  65.        if (guess == number) {
  66.            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Correct!"));
  67.        }
  68.        remainingGuesses--;
  69.        return null;
  70.     }
  71.
  72.     @PostConstruct
  73.     public void reset() {
  74.         this.smallest = 0;
  75.         this.guess = 0;
  76.         this.remainingGuesses = 10;
  77.         this.biggest = maxNumber;
  78.         this.number = randomNumber.get();
  79.     }
  80.
  81.     public void validateNumberRange(FacesContext context,  UIComponent toValidate, Object value) {
  82.         if (remainingGuesses <= 0) {
  83.             FacesMessage message = new FacesMessage("No guesses left!");
  84.             context.addMessage(toValidate.getClientId(context), message);
  85.             ((UIInput)toValidate).setValid(false);
  86.             return;
  87.         }
  88.         int input = (Integer) value;
  89.         if (input < smallest || input > biggest) {
  90.             ((UIInput)toValidate).setValid(false);
  91.             FacesMessage message = new FacesMessage("Invalid guess");
  92.             context.addMessage(toValidate.getClientId(context), message);
  93.         }
  94.     }
  95. }

Notice especially the following highlighted annotations in the bean.

  • The @Named annotation in Line 14. This is a Dependency Injection For Java annotation that is used to associate a name with the bean. Because there is no name specified as an argument to the annotation, the name of the bean will be the name of the JavaBean with its first letter made lowercase, that is, game. The annotation enables the application to reference the bean by that name using the EL expressions in the view.
  • The @SessionScoped annotation in Line 15. This is a CDI annotation that specifies a scope for the bean. All beans have a scope that determines the lifecycle of their instances and which instances of the beans are visible to instances of other beans. The @SessionScoped annotation declares that this bean is a session scoped bean, meaning that its lifecycle is the lifecycle of the session.
  • The @Inject annotation in Line 23 and line 29. This is a CDI annotation that is used to identify a dependency injection point, that is, a point at which a dependency on a Java class or interface can be injected. In line 23, the annotation identifies a dependency injection point for the maxNumber field. Line 23 also specifies a qualifier annotation, @MaxNumber, that identifies the implementation to inject. Qualifiers are strongly-typed keys that help distinguish different uses of objects of the same type. Later in this tip, you'll learn more about qualifiers. Defining @MaxNumber as a qualifier annotation enables the injection of the limit value for the maximum number of guesses. In line 29, the @Inject annotation identifies a dependency injection point for the randomNumber field. Line 29 also specifies a qualifier annotation, @Random, that identifies the implementation to inject. Defining @Random as a qualifier annotation enables the injection of a random number that the user needs to guess.
  • The @PostConstruct annotation in line 72. This annotation is defined in JSR 250, Common Annotations for the Java Platform. The annotation is used to identify a method that will perform initialization after a component is created. Here, the reset()method is marked with a @PostConstruct annotation. After the bean is created, the reset()method initializes a number of variables such as remainingGuesses, which tracks the remaining number of guesses; biggest, which holds the value for the maximum number of guesses; and number, which holds the randomly generated number that the user needs to guess.

Supporting Annotations

You've seen that the bean uses the @Random and @MaxNumber annotations as qualifier annotations. Now let's see how those annotations are defined.

Here is the definition of the @Random annotation:

   1. package weldguess;
   2.
   3. import static java.lang.annotation.ElementType.FIELD;
   4. import static java.lang.annotation.ElementType.METHOD;
   5. import static java.lang.annotation.ElementType.PARAMETER;
   6. import static java.lang.annotation.ElementType.TYPE;
   7. import static java.lang.annotation.RetentionPolicy.RUNTIME;
   8. import java.lang.annotation.Documented;
   9. import java.lang.annotation.Retention;
  10. import javax.inject.Qualifier;
  11.
  12. @Target( { TYPE, METHOD, PARAMETER, FIELD })
  13. @Retention(RUNTIME)
  14. @Documented
  15. @Qualifier
  16. public @interface Random {
  17. }

The @Qualifier annotation in line 15 is a Dependency Injection For Java annotation that is used to identify an annotation as a qualifier annotation. A qualifier identifies a specific implementation of a Java class or interface to be injected. In order to use a qualifier annotation, you first need to define its type as a qualifier. You use the @Qualifier annotation to do that. Defining @Random as a qualifier annotation enables a random number to be injected into the application.

The @Qualifier annotation is also used in the definition of the @MaxNumber annotation, as shown below:

   1. package weldguess;
   2.
   3. import static java.lang.annotation.ElementType.FIELD;
   4. import static java.lang.annotation.ElementType.METHOD;
   5. import static java.lang.annotation.ElementType.PARAMETER;
   6. import static java.lang.annotation.ElementType.TYPE;
   7. import static java.lang.annotation.RetentionPolicy.RUNTIME;
   8. import java.lang.annotation.Documented;
   9. import java.lang.annotation.Retention;
  10. import java.lang.annotation.Target;
  11. import javax.inject.Qualifier;
  12.
  13. @Target( { TYPE, METHOD, PARAMETER, FIELD })
  14. @Retention(RUNTIME)
  15. @Documented
  16. @Qualifier
  17. public @interface MaxNumber {
  18. }

The @Qualifier annotation in line 16 defines @MaxNumber as a qualifier annotation. Defining @MaxNumber as a qualifier annotation enables the injection of the maximum number of allowed guesses into the application.

The Utility Bean

There is one more important component of the application, a utility bean named Generator. Here is what the Generator bean looks like:

   1. package weldguess;
   2.
   3. import java.io.Serializable;
   4. import javax.enterprise.context.ApplicationScoped;
   5. import javax.enterprise.inject.Produces;
   6.
   7. @ApplicationScoped
   8. public class Generator implements Serializable {
   9.     private static final long serialVersionUID = -7213673465118041882L;
  10.    private java.util.Random random = new java.util.Random( System.currentTimeMillis() );
  11.     private int maxNumber = 100;
  12.     java.util.Random getRandom() {
  13.         return random;
  14.     }
  15.     @Produces @Random int next() {
  16.         return getRandom().nextInt(maxNumber);
  17.     }
  18.     @Produces @MaxNumber int getMaxNumber() {
  19.         return maxNumber;
  20.     }
  21. }

Here are what the highlighted annotations in the bean do:

  • The @ApplicationScoped annotation in line 7 is a CDI annotation that specifies a scope for the class. The annotation declares that an instance of the Generator class exists for the lifecycle of the application.
  • The @Produces annotation in line 15 and line 18 is a CDI annotation that is used to identify a method as a producer method. A producer method is called whenever another bean in the application needs an injected object. In line 15, the producer method is next(). The method is called by the Beans Manager when the Game bean needs to obtain an instance of the next random number. In line 18, the producer method is getMaxNumber(). The method is called by the Beans Manager when the Game bean needs to obtain the maximum number of allowed guesses — in this case, 100.

How the Components Work Together

Let's return to the UI discussed earlier. When a user responds to the prompt and clicks the Guess button, CDI technology goes into action. The Java EE container automatically instantiates a contextual instance of the Game bean and the Generator bean. After the Game bean is created, its reset() method is called to initialize a number of variables such as biggest, which holds the value for the maximum number of guesses, and number, which holds the randomly generated number that the user needs to guess.

The Game bean gets the maximum number of guesses from its maxNumber field. Recall that a dependency injection point with the qualifier annotation, @MaxNumber, is specified for the maxNumber field. Recall too that a producer method, getMaxNumber(), in the Generator bean is associated with the @MaxNumber qualifier annotation. As a result, when the Game bean accesses the @MaxNumber field, it calls the getMaxNumber() method in the Generator bean. The getMaxNumber() method returns the value of the maxNumber field, that is, 100.

The Game bean takes a similar route to provide a random number for the user to guess. The bean calls the randomNumber.get() method as part of its post-construct initialization. Recall that a dependency injection point with the qualifier annotation, @Random, is specified for the randomNumber field, and a producer method, getRandom(), in the Generator bean is associated with the @Random qualifier annotation. As a result, when the Game bean calls the randomNumber.get() method, it invokes the getRandom() method in the Generator bean. The randomNumber.get() method uses the getRandom() method of the java.util.Random class to generate a random number within the range 0 to 100.

Running the Sample Code

A sample application accompanies this tip. To run the sample application, do the following:

  1. If you haven't already done so, download a recent promoted build or nightly build of the GlassFish v3 Preview application server.
  2. Download the sample application package, weld-guess.zip
  3. Extract the contents of the sample application package. You should see the WAR file for the application, weld-guess.war, as well as folders for the application source code. The source code for the UI is in the web folder. The source code for the beans and annotations are in the src folder.
  4. Start the GlassFish v3 Preview application server by entering the following command:
       <GFv3_inst>/bin/asadmin start-domain
    
    where <GFv3_inst> is where you installed the GlassFish v3 Preview application server.
  5. Deploy the sample application by copying the weld-guess.war file to the <GFv3inst>/domains/domain1/autodeploy directory.
  6. Execute the application by opening a browser and accessing the URL http://localhost:8080/weld-guess

Further Reading

For more information, see the following resources:

About the Author

Roger Kitain is the JavaServer Faces co-specification lead. He has been extensively involved with server-side web technologies and products since 1997. Roger started working on JavaServer Faces technology in 2001, as a member of the reference implementation team. He has experience with Servlet and JSP technologies. Most recently, Roger has been involved with the CDI specification and integration of CDI with the GlassFish container. Read Roger Kitain's blog.

Comments:

Interesting. Thanks.

Posted by Davide Perini on November 03, 2009 at 03:39 AM PST #

Great post!
Thank you!

Posted by chanel j12 watch on November 08, 2009 at 04:39 PM PST #

Thanks for this great post.

I'm just wondering which part the JSR 330 plays in the game. Se(e/a)ms to me as if your example doesn't rely on any JSR 300 functionality, or am I wrong?

Cheers,
Jan

Posted by Jan Groth on November 09, 2009 at 08:02 PM PST #

It's not clear to me how the Generator is injected. Suppose I write another class named Generator2 with the same annotations as your existing Generator but different implementations, how would the web app know which generator to use?

Posted by David Letterman on November 18, 2009 at 01:04 AM PST #

JSR 330 includes the @inject annotation to inject artifacts. This is used in the application.

Posted by Roger Kitain on December 09, 2009 at 11:57 PM PST #

With respect to the comment about ambiguous producer methods (Generator). Please refer to Section 4.7 in this doc:
http://docs.jboss.org/weld/reference/1.0.0/en-US/html_single/#d0e1297

I could not immediately locate verbage in the 299 spec.

Posted by Roger Kitain on December 10, 2009 at 12:16 AM PST #

Excellent article, thank you !

Posted by Remy Girodon on December 14, 2009 at 05:38 PM PST #

for windows 7 running glassfish v3.0.1, in the "Running the Sample Code" Step 5 from above: The dir should be <GFv3inst>/glassfish/domains/domain1/autodeploy

Posted by Patrick Nelson on October 11, 2010 at 05:02 AM PDT #

Post a Comment:
Comments are closed for this entry.
About

edort

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today