SeamFramework.orgCommunity Documentation

Introduction to Web Beans

The new Java standard for dependency injection and contextual state management


I. Using contextual objects
1. Getting started with Web Beans
1.1. Your first Web Bean
1.2. What is a Web Bean?
1.2.1. API types, binding types and dependency injection
1.2.2. Deployment types
1.2.3. Scope
1.2.4. Web Bean names and Unified EL
1.2.5. Interceptor binding types
1.3. What kinds of objects can be Web Beans?
1.3.1. Simple Web Beans
1.3.2. Enterprise Web Beans
1.3.3. Producer methods
1.3.4. JMS endpoints
2. JSF web application example
3. The Web Beans Reference Implementation
3.1. The numberguess example
3.2. The translator example
4. Dependency injection
4.1. Binding annotations
4.1.1. Binding annotations with members
4.1.2. Combinations of binding annnotations
4.1.3. Binding annotations and producer methods
4.1.4. The default binding type
4.2. Deployment types
4.2.1. Enabling deployment types
4.2.2. Deployment type precedence
4.2.3. Example deployment types
4.3. Fixing unsatisfied dependencies
4.4. Client proxies
4.5. Obtaining a Web Bean by programatic lookup
4.6. Lifecycle callbacks, @Resource, @EJB and @PersistenceContext
4.7. The InjectionPoint object
5. Scopes and contexts
5.1. Scope types
5.2. Built-in scopes
5.3. The conversation scope
5.3.1. Conversation demarcation
5.3.2. Conversation propagation
5.3.3. Conversation timeout
5.4. The dependent pseudo-scope
5.4.1. The @New annotation
6. Producer methods
6.1. Scope of a producer method
6.2. Injection into producer methods
6.3. Use of @New with producer methods
II. Developing loosely-coupled code
7. Interceptors
7.1. Interceptor bindings
7.2. Implementing interceptors
7.3. Enabling interceptors
7.4. Interceptor bindings with members
7.5. Multiple interceptor binding annotations
7.6. Interceptor binding type inheritance
7.7. Use of @Interceptors
8. Decorators
8.1. Delegate attributes
8.2. Enabling decorators
9. Events
9.1. Event observers
9.2. Event producers
9.3. Registering observers dynamically
9.4. Event bindings with members
9.5. Multiple event bindings
9.6. Transactional observers
III. Making the most of strong typing
10. Stereotypes
10.1. Default scope and deployment type for a stereotype
10.2. Restricting scope and type with a stereotype
10.3. Interceptor bindings for stereotypes
10.4. Name defaulting with stereotypes
10.5. Standard stereotypes
11. Specialization
11.1. Using specialization
11.2. Advantages of specialization
12. Defining Web Beans using XML
12.1. Declaring Web Bean classes
12.2. Declaring Web Bean metadata
12.3. Declaring Web Bean members
12.4. Declaring inline Web Beans
12.5. Using a schema
IV. Web Beans and the Java EE ecosystem
13. Java EE integration
13.1. Injecting Java EE resources into a Web Bean
13.2. Calling a Web Bean from a Servlet
13.3. Calling a Web Bean from a Message-Driven Bean
13.4. JMS endpoints
13.5. Packaging and deployment
14. Extending Web Beans
14.1. The Manager object
14.2. The Bean class
14.3. The Context interface
15. Next steps
A. Integrating the Web Beans RI into other environments
A.1. The Web Beans RI SPI
A.2. The contract with the container

The Web Beans (JSR-299) specification defines a set of services for the Java EE environment that makes applications much easier to develop. Web Beans layers an enhanced lifecycle and interaction model over existing Java component types including JavaBeans and Enterprise Java Beans. As a complement to the traditional Java EE programming model, the Web Beans services provide:

Dependency injection, together with contextual lifecycle management, saves the user of an unfamiliar API from having to ask and answer the following questions:

A Web Bean specifies only the type and semantics of other Web Beans it depends upon. It need not be aware of the actual lifecycle, concrete implementation, threading model or other clients of any Web Bean it depends upon. Even better, the concrete implementation, lifecycle and threading model of a Web Bean it depends upon may vary according to the deployment scenario, without affecting any client.

Events, interceptors and decorators enhance the loose-coupling that is inherent in this model:

Most importantly, Web Beans provides all these facilities in a typesafe way. Web Beans never uses string-based identifiers to determine how collaborating objects fit together. And XML, though it remains an option, is rarely used. Instead, Web Beans uses the typing information that is already available in the Java object model, together with a new pattern, called binding annotations, to wire together Web Beans, their dependencies, their interceptors and decorators and their event consumers.

The Web Beans services are general and apply to the following types of components that exist in the Java EE environment:

Web Beans even provides the necessary integration points so that other kinds of components defined by future Java EE specifications or by non-standard frameworks may be cleanly integrated with Web Beans, take advantage of the Web Beans services, and interact with any other kind of Web Bean.

Web Beans was influenced by a number of existing Java frameworks, including Seam, Guice and Spring. However, Web Beans has its own very distinct character: more typesafe than Seam, more stateful and less XML-centric than Spring, more web and enterprise-application capable than Guice.

Most importantly, Web Beans is a JCP standard that integrates cleanly with Java EE, and with any Java SE environment where embeddable EJB Lite is available.

So you're already keen to get started writing your first Web Bean? Or perhaps you're skeptical, wondering what kinds of hoops the Web Beans specification will make you jump through! The good news is that you've probably already written and used hundreds, perhaps thousands of Web Beans. You might not even remember the first Web Bean you wrote.

With certain, very special exceptions, every Java class with a constructor that accepts no parameters is a Web Bean. That includes every JavaBean. Furthermore, every EJB 3-style session bean is a Web Bean. Sure, the JavaBeans and EJBs you've been writing every day have not been able to take advantage of the new services defined by the Web Beans specification, but you'll be able to use every one of them as Web Beans—injecting them into other Web Beans, configuring them via the Web Beans XML configuration facility, even adding interceptors and decorators to them—without touching your existing code.

Suppose that we have two existing Java classes, that we've been using for years in various applications. The first class parses a string into a list of sentences:

public class SentenceParser {

    public List<String> parse(String text) { ... }
}

The second existing class is a stateless session bean front-end for an external system that is able to translate sentences from one language to another:

@Stateless

public class SentenceTranslator implements Translator {
    public String translate(String sentence) { ... }
}

Where Translator is the local interface:

@Local

public interface Translator {
    public String translate(String sentence);
}

Unfortunately, we don't have a preexisting class that translates whole text documents. So let's write a Web Bean that does this job:

public class TextTranslator {

    
    private SentenceParser sentenceParser;
    private Translator sentenceTranslator;
    
    @Initializer
    TextTranslator(SentenceParser sentenceParser, Translator sentenceTranslator) {
        this.sentenceParser = sentenceParser;
        this.sentenceTranslator = sentenceTranslator;
    }
    
    public String translate(String text) {
        StringBuilder sb = new StringBuilder();
        for (String sentence: sentenceParser.parse(text)) {
            sb.append(sentenceTranslator.translate(sentence));
        }
        return sb.toString();
    }
    
}

We may obtain an instance of TextTranslator by injecting it into a Web Bean, Servlet or EJB:

@Initializer

public setTextTranslator(TextTranslator textTranslator) {
    this.textTranslator = textTranslator;
}

Alternatively, we may obtain an instance by directly calling a method of the Web Bean manager:

TextTranslator tt = manager.getInstanceByType(TextTranslator.class);

But wait: TextTranslator does not have a constructor with no parameters! Is it still a Web Bean? Well, a class that does not have a constructor with no parameters can still be a Web Bean if it has a constructor annotated @Initializer.

As you've guessed, the @Initializer annotation has something to do with dependency injection! @Initializer may be applied to a constructor or method of a Web Bean, and tells the Web Bean manager to call that constructor or method when instantiating the Web Bean. The Web Bean manager will inject other Web Beans to the parameters of the constructor or method.

At system initialization time, the Web Bean manager must validate that exactly one Web Bean exists which satisfies each injection point. In our example, if no implementation of Translator available—if the SentenceTranslator EJB was not deployed—the Web Bean manager would throw an UnsatisfiedDependencyException. If more than one implementation of Translator was available, the Web Bean manager would throw an AmbiguousDependencyException.

So what, exactly, is a Web Bean?

A Web Bean is an application class that contains business logic. A Web Bean may be called directly from Java code, or it may be invoked via Unified EL. A Web Bean may access transactional resources. Dependencies between Web Beans are managed automatically by the Web Bean manager. Most Web Beans are stateful and contextual. The lifecycle of a Web Bean is always managed by the Web Bean manager.

Let's back up a second. What does it really mean to be "contextual"? Since Web Beans may be stateful, it matters which bean instance I have. Unlike a stateless component model (for example, stateless session beans) or a singleton component model (such as servlets, or singleton beans), different clients of a Web Bean see the Web Bean in different states. The client-visible state depends upon which instance of the Web Bean the client has a reference to.

However, like a stateless or singleton model, but unlike stateful session beans, the client does not control the lifecycle of the instance by explicitly creating and destroying it. Instead, the scope of the Web Bean determines:

For a given thread in a Web Beans application, there may be an active context associated with the scope of the Web Bean. This context may be unique to the thread (for example, if the Web Bean is request scoped), or it may be shared with certain other threads (for example, if the Web Bean is session scoped) or even all other threads (if it is application scoped).

Clients (for example, other Web Beans) executing in the same context will see the same instance of the Web Bean. But clients in a different context will see a different instance.

One great advantage of the contextual model is that it allows stateful Web Beans to be treated like services! The client need not concern itself with managing the lifecycle of the Web Bean it is using, nor does it even need to know what that lifecyle is. Web Beans interact by passing messages, and the Web Bean implementations define the lifecycle of their own state. The Web Beans are loosely coupled because:

We can replace one Web Bean with a different Web Bean that implements the same API and has a different lifecycle (a different scope) without affecting the other Web Bean implementation. In fact, Web Beans defines a sophisticated facility for overriding Web Bean implementations at deployment time, as we will see in Section 4.2, “Deployment types”.

Note that not all clients of a Web Bean are Web Beans. Other objects such as Servlets or Message-Driven Beans—which are by nature not injectable, contextual objects—may also obtain references to Web Beans by injection.

Enough hand-waving. More formally, according to the spec:

A Web Bean comprises:

  • A (nonempty) set of API types

  • A (nonempty) set of binding annotation types

  • A scope

  • A deployment type

  • Optionally, a Web Bean name

  • A set of interceptor binding types

  • A Web Bean implementation

Let's see what some of these terms mean, to the Web Bean developer.

Web Beans usually acquire references to other Web Beans via dependency injection. Any injected attribute specifies a "contract" that must be satisfied by the Web Bean to be injected. The contract is:

An API is a user-defined class or interface. (If the Web Bean is an EJB session bean, the API type is the @Local interface or bean-class local view). A binding type represents some client-visible semantic that is satisfied by some implementations of the API and not by others.

Binding types are represented by user-defined annotations that are themselves annotated @BindingType. For example, the following injection point has API type PaymentProcessor and binding type @CreditCard:

@CreditCard PaymentProcessor paymentProcessor

If no binding type is explicitly specified at an injection point, the default binding type @Current is assumed.

For each injection point, the Web Bean manager searches for a Web Bean which satisfies the contract (implements the API, and has all the binding types), and injects that Web Bean.

The following Web Bean has the binding type @CreditCard and implements the API type PaymentProcessor. It could therefore be injected to the example injection point:

@CreditCard

public class CreditCardPaymentProcessor 
    implements PaymentProcessor { ... }

If a Web Bean does not explicitly specify a set of binding types, it has exactly one binding type: the default binding type @Current.

Web Beans defines a sophisticated but intuitive resolution algorithm that helps the container decide what to do if there is more than one Web Bean that satisfies a particular contract. We'll get into the details in Chapter 4, Dependency injection.

We've already seen that JavaBeans, EJBs and some other Java classes can be Web Beans. But exactly what kinds of objects are Web Beans?

The specification says that all EJB 3-style session and singleton beans are enterprise Web Beans. Message driven beans are not Web Beans—since they are not intended to be injected into other objects—but they can take advantage of most of the functionality of Web Beans, including dependency injection and interceptors.

Every local interface of an enterprise Web Bean that does not have a wildcard type parameter or type variable, and every one of its superinterfaces, is an API type of the enterprise Web Bean. If the EJB bean has a bean class local view, the bean class, and every one of its superclasses, is also an API type.

Stateful session beans should declare a remove method with no parameters or a remove method annotated @Destructor. The Web Bean manager calls this method to destroy the stateful session bean instance at the end of its lifecycle. This method is called the destructor method of the enterprise Web Bean.

@Stateful @SessionScoped

public class ShoppingCart {
    ...
    
    @Remove
    public void destroy() {}
}

So when should we use an enterprise Web Bean instead of a simple Web Bean? Well, whenever we need the advanced enterprise services offered by EJB, such as:

we should use an enterprise Web Bean. When we don't need any of these things, a simple Web Bean will serve just fine.

Many Web Beans (including any session or application scoped Web Bean) are available for concurrent access. Therefore, the concurrency management provided by EJB 3.1 is especially useful. Most session and application scoped Web Beans should be EJBs.

Web Beans which hold references to heavy-weight resources, or hold a lot of internal state benefit from the advanced container-managed lifecycle defined by the EJB @Stateless/@Stateful/@Singleton model, with its support for passivation and instance pooling.

Finally, it's usually obvious when method-level transaction management, method-level security, timers, remote methods or asynchronous methods are needed.

It's usually easy to start with simple Web Bean, and then turn it into an EJB, just by adding an annotation: @Stateless, @Stateful or @Singleton.

Let's illustrate these ideas with a full example. We're going to implement user login/logout for an application that uses JSF. First, we'll define a Web Bean to hold the username and password entered during login:

@Named @RequestScoped

public class Credentials {
    
    private String username;
    private String password;
    
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    
    public String getPassword() { return password; }
    public void setPassword(String password) { this.password = password; }
    
}

This Web Bean is bound to the login prompt in the following JSF form:

<h:form>

    <h:panelGrid columns="2" rendered="#{!login.loggedIn}">
        <h:outputLabel for="username">Username:</h:outputLabel>
        <h:inputText id="username" value="#{credentials.username}"/>
        <h:outputLabel for="password">Password:</h:outputLabel>
        <h:inputText id="password" value="#{credentials.password}"/>
    </h:panelGrid>
    <h:commandButton value="Login" action="#{login.login}" rendered="#{!login.loggedIn}"/>
    <h:commandButton value="Logout" acion="#{login.logout}" rendered="#{login.loggedIn}"/>
</h:form>

The actual work is done by a session scoped Web Bean that maintains information about the currently logged-in user and exposes the User entity to other Web Beans:

@SessionScoped @Named

public class Login {
    @Current Credentials credentials;
    @PersistenceContext EntityManager userDatabase;
    private User user;
    
    public void login() {
        
        List<User> results = userDatabase.createQuery(
           "select u from User u where u.username=:username and u.password=:password")
           .setParameter("username", credentials.getUsername())
           .setParameter("password", credentials.getPassword())
           .getResultList();
        
        if ( !results.isEmpty() ) {
           user = results.get(0);
        }
        
    }
    
    public void logout() {
        user = null;
    }
    
    public boolean isLoggedIn() {
       return user!=null;
    }
    
    @Produces @LoggedIn User getCurrentUser() {
        return user;
    }
}

Of course, @LoggedIn is a binding annotation:

@Retention(RUNTIME)

@Target({TYPE, METHOD, FIELD})
@BindingType
public @interface LoggedIn {}

Now, any other Web Bean can easily inject the current user:

public class DocumentEditor {


    @Current Document document;
    @LoggedIn User currentUser;
    @PersistenceContext EntityManager docDatabase;
    
    public void save() {
        document.setCreatedBy(currentUser);
        docDatabase.persist(document);
    }
    
}

Hopefully, this example gives a flavor of the Web Bean programming model. In the next chapter, we'll explore Web Beans dependency injection in greater depth.

The Web Beans Reference Implementation is being developed at the Seam project. You can download the latest developer release of Web Beans from the the downloads page.

The Web Beans RI comes with a two deployable example applications: webbeans-numberguess, a war example, containing only simple beans, and webbeans-translator an ear example, containing enterprise beans. To run the examples you'll need the following:

  • the latest release of the Web Beans RI,

  • JBoss AS 5.0.0.GA, and

  • Ant 1.7.0.

Currently, the Web Beans RI only runs on JBoss Application Server 5. You'll need to download JBoss AS 5.0.0.GA from jboss.org, and unzip it. For example:

$ cd /Applications
$ unzip ~/jboss-5.0.0.GA.zip

Next, download the Web Beans RI from seamframework.org, and unzip it. For example

$ cd ~/
$ unzip ~/webbeans-1.0.0.ALPHA1.zip

Next, we need to tell Web Beans where JBoss is located. Edit jboss-as/build.properties and set the jboss.home property. For example:

jboss.home=/Applications/jboss-5.0.0.GA

As Web Beans is a new piece of software, you need to update JBoss AS to run the Web Beans RI. Future versions of JBoss AS will include these updates, and this step won't be necessary.

Note

Currently, two updates are needed. Firstly, a new deployer, webbeans.deployer is added. This adds supports for Web Bean archives to JBoss AS, and allows the Web Beans RI to query the EJB3 container and discover which EJBs are installed in your application. Secondly, an update to JBoss EJB3 is needed.

To install the update, you'll need Ant 1.7.0 installed, and the ANT_HOME environment variable set. For example:

$ unzip apache-ant-1.7.0.zip
$ export ANT_HOME=~/apache-ant-1.7.0

Then, you can install the update. The update script will use Maven to download the Web Beans and EJB3 automatically.

$ cd webbeans-1.0.0.ALPHA1/jboss-as
$ ant update

Now, you're ready to deploy your first example!

Tip

The build scripts for the examples offer a number of targets, these are:

  • ant restart - deploy the example in exploded format

  • ant explode - update an exploded example, without restarting the deployment

  • ant deploy - deploy the example in compressed jar format

  • ant undeploy - remove the example from the server

  • ant clean - clean the example

To deploy the numberguess example:

$ cd examples/numberguess
ant deploy

Wait for the application to deploy, and enjoy hours of fun at http://localhost:8080/webbeans-numberguess!

The Web Beans RI includes a second simple example that will translate your text into Latin. The numberguess example is a war example, and uses only simple beans; the translator example is an ear example, and includes enterprise beans, packaged in an EJB module. To try it out:

$ cd examples/translator
ant deploy

Wait for the application to deploy, and visit http://localhost:8080/webbeans-translator!

In the numberguess application you get given 10 attempts to guess a number between 1 and 100. After each attempt, you will be told whether you are too high, or too low.

The numberguess example is comprised of a number of Web Beans, configuration files, and Facelet JSF pages, packaged as a war. Let's start with the configuration files.

All the configuration files for this example are located in WEB-INF/, which is stored in WebContent in the source tree. First, we have faces-config.xml, in which we tell JSF to use Facelets:


<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="1.2"
              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/web-facesconfig_1_2.xsd">
    
    <application>
        <view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
    </application>

</faces-config>

There is an empty web-beans.xml file, which marks this application as a Web Beans application.

Finally there is web.xml:

Let's take a look at the Facelet view:

<!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:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:s="http://jboss.com/products/seam/taglib">

  <ui:composit(1)ion template="template.xhtml">
    <ui:define name="content">
       <h1>Guess a number...</h1>
       <h:form id="NumberGuessMain">
          <div(2) style="color: red">
             <h:messages id="messages" globalOnly="false"/>
             <h:outputText id="Higher" value="Higher!" rendered="#{game.number gt game.guess}"/>
             <h:outputText id="Lower" value="Lower!" rendered="#{game.number lt game.guess}"/>
          </div>
   
          <div>
             I(3)'m thinking of a number between #{game.smallest} and #{game.biggest}.
             You have #{game.remainingGuesses} guesses.
          </div>
     
          <div>
             Your guess: 
             <(4)h:inputText id="inputGuess" 
                          value="#{game.guess}" 
                          required="true" 
                          size="3">
              (5)  <f:validateLongRange maximum="#{game.biggest}" 
                                     minimum="#{game.smallest}"/>
             </h:inputText>
            <h(6):commandButton id="GuessButton" 
                             value="Guess" 
                             action="#{game.check}"/>
          </div>
   
       </h:form>
    </ui:define>
  </ui:composition>
</html>
1

Facelets is a templating language for JSF, here we are wrapping our page in a template which defines the header.

2

There are a number of messages which can be sent to the user, "Higher!", "Lower!" and "Correct!"

3

As the user guesses, the range of numbers they can guess gets smaller - this sentance changes to make sure they know what range to guess in.

4

This input field is bound to a Web Bean, using the value expression.

5

A range validator is used to make sure the user doesn't accidentally input a number outside of the range in which they can guess - if the validator wasn't here, the user might use up a guess on an out of range number.

6

And, of course, there must be a way for the user to send their guess to the server. Here we bind to an action method on the Web Bean.

The example exists of 4 classes, the first two of which are binding types. First, there is the @Random binding type, used for injecting a random number:

@Target( { TYPE, METHOD, PARAMETER, FIELD })

@Retention(RUNTIME)
@Documented
@BindingType
public @interface Random {}

There is also the @MaxNumber binding type, used for injecting the maximum number that can be injected:

@Target( { TYPE, METHOD, PARAMETER, FIELD })

@Retention(RUNTIME)
@Documented
@BindingType
public @interface MaxNumber {}

The Generator class is responsible for creating the random number, via a producer method. It also exposes the maximum possible number via a producer method:

@ApplicationScoped

public class Generator {
   
   private java.util.Random random = new java.util.Random( System.currentTimeMillis() );
   
   private int maxNumber = 100;
   
   java.util.Random getRandom()
   {
      return random;
   }
   
   @Produces @Random int next() { 
      return getRandom().nextInt(maxNumber); 
   }
   
   @Produces @MaxNumber int getMaxNumber()
   {
      return maxNumber;
   }
}

You'll notice that the Generator is application scoped; therefore we don't get a different random each time.

The final Web Bean in the application is the session scoped Game. By making Game session scoped, you can only play the game once per browser session. You could easily add a reset button - a good exercise for the reader :-)

You'll also note that we've used the @Named annotation, so that we can use the bean through EL in the JSF page. Finally, we've used constructor injection to initialize the game with a random number. And of course, we need to tell the player when they've won, so we give feedback with a FacesMessage.

@Named

@SessionScoped
public class Game {
   private int number;
   
   private int guess;
   private int smallest;
   private int biggest;
   private int remainingGuesses;
   
   public Game() {}
   
   @Initializer
   Game(@Random int number, @MaxNumber int maxNumber) {
      this.number = number;
      this.smallest = 1;
      this.biggest = maxNumber;
      this.remainingGuesses = 10;
   }
   // Getters and setters for fields
   
   public String check() {
      if (guess>number) {
         biggest = guess - 1;
      }
      if (guess<number) {
         smallest = guess + 1;
      }
      if (guess == number) {
         FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Correct!"));
      }
      remainingGuesses--;
      return null;
   }
   
}