Help

Inactive Bloggers
24. Sep 2007, 18:41 CET, by Pete Muir

Those of you who keep up with Seam CVS will have noticed a lot of changes recently to the build system. I'm going to give a brief tour of them here, and discuss how they affect you when you use Seam.

Maven

We chose to use Maven to manage Seam dependencies as it offers reasonable support for transitive dependencies, it's emerging as the de-facto standard both in the Java Enterprise world and at JBoss.org, tools like Ant (using Maven Ant Tasks) and Ivy can consume Maven POMs and the 40 odd votes for publishing Seam to Maven pushed us somewhat ;)

Using Seam with Maven

For those Maven pro's among you, here's the essential information to get you started. If you are considering dabbling with Maven, then you might want to wait for a couple of weeks for Seam 2.0.0.GA which will include a brief tutorial on using Maven with Seam.

To checkout the support you'll need to add http://snapshots.jboss.org/maven2 as a snapshot repository to your POM; having done this, you can add Seam to your projects POM:

<dependency>
  <groupId>org.jboss.seam</groupId>
  <artifactId>jboss-seam</artifactId>
</dependency>
<dependency>
  <groupId>org.jboss.seam</groupId>
  <artifactId>jboss-seam-ui</artifactId>
</dependency>
<dependency>
  <groupId>org.jboss.seam</groupId>
  <artifactId>jboss-seam-pdf</artifactId>
</dependency>
<dependency>
  <groupId>org.jboss.seam</groupId>
  <artifactId>jboss-seam-remoting</artifactId>
</dependency>
<dependency>
  <groupId>org.jboss.seam</groupId>
  <artifactId>jboss-seam-ioc</artifactId>
</dependency>
<dependency>
  <groupId>org.jboss.seam</groupId>
  <artifactId>jboss-seam-ioc</artifactId>
</dependency>

We'll shortly start publishing snapshots from our continuous integration server.

As Seam is an integration framework we spend a lot of time ensuring that the integrated libraries work well, both together and with Seam. If you want to always use the recommended library versions with Seam you may want to use the Seam root POM (which just declares repositories and dependencyManagement) as your parent POM:

<parent>
  <groupId>org.jboss.seam</groupId>
  <artifactId>root</artifactId>
  <version>2.0.0.SNAPSHOT</version>
</parent>

You can then just declare your dependencies versionless in your POM:

<dependency>
  <groupId>org.jboss.seam</groupId>
  <artifactId>jboss-seam</artifactId>
</dependency>

<dependency>
  <groupId>org.jboss.seam</groupId>
  <artifactId>jboss-seam-ui</artifactId>
</dependency>

<dependency>
  <groupId>org.jbpm</groupId>
  <artifactId>jbpm-jpdl</artifactId>
</dependency>

You'll then get the SNAPSHOT of Seam core and ui and jBPM jPDL 3.2.1.

Maven/Dependency Management Roadmap

So, what else is on the cards?

  • Write some documentation!
  • Release the next version of Seam to http://repository.jboss.org
  • Improve the handling of JBoss Embedded in the build scripts
  • Alter the seamdiscs example to use Maven for dependency management
  • Provide an option to use Maven-based dependency management in seam-gen

Feedback and Troubleshooting

Whilst this is a good start there a still some bumps to iron out! I'm really hoping for strong (and fast!) feedback on this one to get it just right for the GA.

I've got a couple of questions/discussion points for you maven experts out there:

  • Is there a way to read external properties into maven? As we're using Maven Ant Tasks we can't use -Dproperty=foo and we need to be able to specify the property file relative to the POM's location.
  • Is there a nice way to provide dependency profiles without putting empty jars all over the place?
  • Is using the Seam root POM as your parent POM an acceptable alternative?

If you are following Seam CVS and are having problems building Seam:

  1. Grab the latest CVS
  2. Make sure your build.properties file /doesn't/ contain any references to version or patchlevel
  3. Run ant clean build
  4. If the build fails due to
    The <urn:maven-artifact-ant:dependencies> type doesn't support the "versionsid" attribute.
    you probably have an older version of maven-ant-tasks.jar on your classpath. Try running ant -nouserlib or update Maven Ant Tasks to 2.0.7.
  5. Report your problem on the forum!

Maven in Seam

So, how did we use Seam and Maven Ant Tasks to build Seam?

Building Seam using Maven

However good Maven is at dependency management, we're not in love with using Maven for building software (it seems slow, and incredibly restrictive) so we decided on the half-way house of using Maven Ant Tasks - which are essentially a set of ant tasks that call into Maven. With this, we can use Ant to load dependencies from Maven POM files and publish artifacts to Maven.

In CVS the Seam build uses Maven POM files to declare dependencies. These pom files are stored in /build and, using some Ant substitution filters, are copied to /classes/poms. From these poms, we can build path's (to compile Seam), fileset's (using the Maven Ant Tasks) and can use the handy version mapper (which allows you to copy a dependency fileset stripping off the version information e.g. org/jboss/seam/jboss-el/2.0.0.BETA1/jboss-el-2.0.0.BETA1.jar is copied to /jboss-el.jar).

There is more information in /build/readme.txt

Examples and Seam-gen

For now we've stuck with using ant for building and declaring the dependencies in the build.xml. To do this we copy Seam and it's dependencies (resolved using Maven) to a staging area, and then building the deployable artifacts against this directory. This has the advantage of not having to write POM's for every example (non-trivial given the lack of dependency profiles in Maven).

/This is the fourth installment of a series of articles describing the current status of the Web Beans specification. Please read the first, second and third installments first./

So far, we've seen a few examples of /scope type annotations/. The scope of a component determines the lifecycle of the component instances, and makes a particular instance visible to all components executing in a particular context.

For example, if we have a session scoped component, CurrentUser, all components that are called in the context of the same HttpSession will see the same instance of CurrentUser. This instance will be automatically created the first time a CurrentUser is needed in that session, and automatically destroyed when the session ends.

Scope types

Web Beans features an /extensible context model/. It is possible to define new scopes by creating a new scope type annotation:

@Retention(RUNTIME)
@Target({TYPE, METHOD})
@ScopeType
public @interface ClusterScoped {}

Of course, that's the easy part of the job. For this scope type to be useful, we will also need to define a Context object that implements the scope! Implementing a Context is usually a very technical task, intended for framework development only.

We can apply a scope type annotation to a Web Bean implementation class to specify the scope of the component:

@ClusterScoped @Component
public class SecondLevelCache { ... }

We can even use the scope type to obtain an instance of the Context object for the scope:

Component<SecondLevelCache> cacheComponent = container.resolveByType(SecondLevelCache.class);
SecondLevelCache cache = container.getContext(ClusterScoped.class).get(cacheComponent);

Built-in scopes

Web Beans defines four built-in scopes:

  • @RequestScoped
  • @SessionScoped
  • @ApplicationScoped
  • @ConversationScoped

For a web application that uses Web Beans:

  1. any servlet has access to active request, session and application contexts
  2. any JSF request has access to an active conversation context

If the application tries to use a component with a scope that does not have an active context, a ContextNotActive exception is thrown by the Web Beans container at runtime.

The dependent psuedo-scope

In addition, there is the notion of the /dependent psuedo-scope/. We use the term psuedo-scope because there is no Context for this special scope.

A Web Bean may be declared to be a @Dependent component:

@Dependent @Component
public class Calculator { ... }

If this case, a new instance of the component is created each time the Web Beans container injects it. This means that any instance of a dependent component is bound to the object into which it was injected. Different clients always see different instances of a dependent component, no matter what context they execute in.

A open issue that currently exists in the Web Beans specification is the question of the default scope for a Web Bean component that does not explicitly declare a scope type. We've narrowed the options down to @RequestScoped and @Dependent, each of which has advantages and disadvantages.

Implicit dependent components

The built-in @New binding annotation allows /implicit/ definition of a dependent component at an injection point. Suppose we declare the following injected attribute:

@In @New Calculator calculator

Then a component with component type @Component, scope @Dependent, binding annotation @New, API type Calculator and implementation class Calculator is implicitly defined.

This is true even if Calculator is /already/ declared as a Web Beans component, for example:

@ConversationScoped @Component
public class Calculator { ... }

So the following injected attributes each get a different instance of Calculator:

@Component 
public class PaymentCalc {

    @In Calculator calculator;
    @In @New Calculator newCalculator;

}

The calculator field has a conversation-scoped instance of Calculator injected. The newCalculator field has a new instance of Calculator injected, with a lifecycle that is bound to the owning PaymentCalc.

This feature is particularly useful with resolver methods.

Resolver methods

According to the spec:

A Web Beans resolver method acts as a source of objects to be injected, where:
  • the objects to be injected are not required to be instances of Web Beans components,
  • the concrete type of the objects to be injected may vary at runtime or
  • the objects require some custom initialization that is not performed by the Web Bean constructor

For example, resolver methods let us:

  • expose a JPA entity as a Web Bean component
  • expose a JDK class as a Web Bean component
  • define multiple Web Bean components, with different scopes or initialization, for the same implementation class
  • vary a Web Bean component implementation class at runtime

In particular, resolver methods let us use runtime polymorphism with Web Beans. As we've seen, component types are a powerful solution to the problem of deployment-time polymorphism. But once the system is deployed, the component implementation is fixed. A resolver method has no such limitation:

@SessionScoped @Component
public class Preferences {
    
    private PaymentStrategyType paymentStrategy;
    
    ...
    
    @Resolves @Preferred @ApplicationScoped
    public PaymentStrategy getPaymentStrategy() {
        switch (paymentStrategy) {
            case CREDIT_CARD: return new CreditCardPaymentStrategy();
            case CHEQUE: return new ChequePaymentStrategy();
            case PAYPAL: return new PayPalPaymentStrategy();
            default: return null;
        } 
    }
    
}

Consider this injection point:

@In @Preferred PaymentStrategy paymentStrat;

When the container injects this field, the resolver method will be called and the returned PaymentStrategy will be injected into the field and bound to the application context. The resolver method won't be called again in the same application context. On the other hand, if we were to remove the @ApplicationScoped annotation:

@Resolves @Preferred
public PaymentStrategy getPaymentStrategy() {
    ...
}

Then the resolver method defaults to dependent scope, and it will be called /every time/ the field is injected!

Injection into resolver methods

There's one problem with this code. If CreditCardPaymentStrategy is a Web Bean component, it should be created by the Web Beans container, not by calling new. We can solve this problem by using injection into the resolver method:

@Resolves @Preferred @ApplicationScoped
public PaymentStrategy getPaymentStrategy(CreditCardPaymentStrategy ccps,
                                          ChequePaymentStrategy cps,
                                          PayPalPaymentStrategy ppps) {
    switch (paymentStrategy) {
        case CREDIT_CARD: return ccps;
        case CHEQUE: return cps;
        case PAYPAL: return ppps;
        default: return null;
    } 
}

Wait, what if CreditCardPaymentStrategy is a request scoped component? Then the resolver method has the effect of promoting the current request scoped instance into application scope. This is almost certainly a bug. We can fix the bug using the special @New binding annotation described above:

@Resolves @Preferred @ApplicationScoped
public PaymentStrategy getPaymentStrategy(@New CreditCardPaymentStrategy ccps,
                                          @New ChequePaymentStrategy cps,
                                          @New PayPalPaymentStrategy ppps) {
    switch (paymentStrategy) {
        case CREDIT_CARD: return ccps;
        case CHEQUE: return cps;
        case PAYPAL: return ppps;
        default: return null;
    } 
}

Then a new /dependent/ instance of CreditCardPaymentStrategy will be created, passed to the resolver method, returned by the resolver and finally bound to the application context.

/This is the third installment of a series of articles describing the current status of the Web Beans specification. You can find the first installment here and the second installment here./

So far, we've seen plenty of examples of components declared using annotations. However, there are a couple of occasions when we can't use annotations to define the component:

  • when the implementation class comes from some pre-existing library
  • when there should be multiple components with the same implementation class

In either of these cases, Web Beans gives us two options:

  • write a /resolver method/
  • declare the component using XML

In a future installment we'll talk more about all the crazy kinds of things we can do with resolver methods. First, let's just prove that Web Beans is not entirely annotation-centric.

We can declare a component in web-beans.xml:

<component>
    <class>java.util.Date</class>
</component>

Then an instance of Date could be injected by another component:

@In Date date;

By default, any component declared in XML has the component type @Component. We can use a custom component type:

<component>
    <class>java.util.Date</class>
    <type>org.jboss.test.Mock</type>
</component>

We can override the default component name:

<component>
    <class>java.util.Date</class>
    <name>now</name>
</component>

Or we can specify a scope for the component:

<component>
    <class>java.util.Date</class>
    <scope>javax.webbeans.SessionScoped</scope>
</component>

We can even specify binding annotations using XML, to distinguish between multiple components with the same implementation class:

<component>
    <class>java.util.Date</class>
    <name>now</name>
    <binding>org.jboss.eg.Now</binding>
</component>

<component>
    <class>java.util.Date</class>
    <name>logInTime</name>
    <scope>javax.webbeans.SessionScoped</scope>
    <binding>org.jboss.eg.LogInTime</binding>
</component>

<component>
    <class>java.util.Date</class>
    <name>startupTime</name>
    <scope>javax.webbeans.ApplicationScoped</scope>
    <binding>org.jboss.eg.StartupTime</binding>
</component>

Where @Now, @LogIn and @Startup are binding annotations used at the injection points:

@In @Now Date currentTime;
@In @LogInTime Date loginTime
@In @StartupTime Date startupTime

As usual, a component may support multiple binding annotations:

<component>
    <class>org.jboss.eg.AsynchronousChequePaymentProcessor</class>
    <binding>org.jboss.eg.PayByCheque</binding>
    <binding>org.jboss.eg.Asynchronous</binding>
</component>

Eventually, Web Beans will also support XML-based configuration of component properties, using literal or EL values, something like this:

<component>
    <class>org.jboss.framework.Captcha</class>
    <property name="strategy">math2digit</property>
    <property name="language">#{user.language}</property>
</component>

However, we've not yet worked out all the details of this. In particular, I would love to support a namespaced approach to component configuration, as seen in Seam or Spring 2.0.

/This is the second installment of a series of articles describing the current status of the Web Beans specification. You can find the first installment here./

Web Beans supports three primary mechanisms for dependency injection:

Direct field injection:

@Component
public class Checkout {

    @In ShoppingCart cart;
    
}

Method injection:

@Component
public class Checkout {
        
    private ShoppingCart cart;

    @In void setShoppingCart(ShoppingCart cart) {
        this.cart = cart;
    }
    
}

And constructor injection:

@Component
public class Checkout {
        
    private final ShoppingCart cart;

    public Checkout(ShoppingCart cart) {
        this.cart = cart;
    }

}

In addition, resolver methods support parameter injection:

@Resolves Checkout createCheckout(ShoppingCart cart) {
    return new Checkout(cart);
}

Dependency injection always occurs when the component instance is first instantiated.

The Web Beans specification defines a procedure, called the /typesafe resolution algorithm/, that the Web Beans container follows when identifying the component to inject to an injection point. This algorithm looks complex at first, but once you understand it, it's really quite intuitive. Typesafe resolution is performed at system initialization time, which means that the container will inform the user immediately if a component's dependencies cannot be satisfied.

The purpose of this algorithm is to allow multiple components to implement the same API type and either:

  • allow the client to select which implementation it requires using /binding annotations/, or
  • allow one implementation of an API to override another implementation of the same API at deployment time, without changes to the client, using /component type precedence/.

Let's explore how the Web Beans container determines a component to be injected.

Binding annotations

If we have more than one component that implements a particular API type, the injection point can specify exactly which component should be injected using a binding annotation. For example, there might be two implementations of PaymentProcessor:

@Component @PayByCheque
public class ChequePaymentProcessor implements PaymentProcessor {
    public void process(Payment payment) { ... }
}

@Component @PayByCreditCard
public class CreditCardPaymentProcessor implements PaymentProcessor {
    public void process(Payment payment) { ... }
}

Where @PayByCheque and @PayByCreditCard are binding annotations:

@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD})
@BindingType
public @interface PayByCheque {}
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD})
@BindingType
public @interface PayByCreditCard {}

A client component developer uses the binding annotation to specify exactly which component should be injected:

@In @PayByCheque PaymentProcessor chequePaymentProcessor;
@In @PayByCreditCard PaymentProcessor creditCardPaymentProcessor;

Equivalently, using constructor injection:

public Checkout(@PayByCheque PaymentProcessor chequePaymentProcessor, 
                 @PayByCreditCard PaymentProcessor creditCardPaymentProcessor) {
   this.chequePaymentProcessor = chequePaymentProcessor;
   this.creditCardPaymentProcessor = creditCardPaymentProcessor;
}

Binding annotations with members

Binding annotations may have members:

@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD})
@BindingType
public @interface PayBy {
    PaymentType value();
}

In which case, the member value is significant:

@In @PayBy(CHEQUE) PaymentProcessor chequePaymentProcessor;
@In @PayBy(CREDIT_CARD) PaymentProcessor creditCardPaymentProcessor;

Combinations of binding annnotations

An injection point may even specify multiple binding annotations:

@In @Asynchronous @PayByCheque paymentProcessor

In this case, only a component which supports /both/ binding annotation would be eligable for injection.

Binding annotations and resolver methods

Of course, even resolver methods may specify binding annotations:

@Resolves 
@Asynchronous @PayByCheque 
PaymentProcessorService createAsyncPaymentProcessor(@PayByCheque PaymentProcessor processor) {
    return new AsynchronousPaymentProcessorService(processor);
}

This method would be called to create an instance for injection to the following injection point:

@In @Asynchronous @PayByCheque PaymentProcessorService service;

Component types

All Web Bean components have a /component type/. Each component type identifies a set of components that should be conditionally installed in some deployments of the system.

For example, we could define a component type named @Mock, which would identify components that should only be installed when the system executes inside an integration testing environment:

@Retention(RUNTIME)
@Target({TYPE, METHOD})
@ComponentType
public @interface Mock {}

Suppose we had some component that interacted with an external system to process payments:

@Component
public class PaymentProcessor {
        
    public void process(Payment p) {
        ...
    }
    
}

For integration or unit testing, the external system is slow or unavailable. So we would create a mock object:

@Mock 
public class MockPaymentProcessor extends PaymentProcessor {

    @Override
    public void process(Payment p) {
        p.setSuccessful(true);
    }

}

Installing component types

Web Beans defines two built-in component types: @Component and @Standard. By default, only components with the built-in component types are installed when the system is deployed. We can identify additional component types to be installed in a particular deployment by listing them in web-beans.xml.

Going back to our example, when we deploy our integration tests, we want all our @Mock objects to be installed:

<web-beans>
    <component-types>
        <component-type>javax.webbeans.Standard</component-type>
        <component-type>javax.webbeans.Component</component-type>
        <component-type>org.jboss.test.Mock</component-type>
    </component-types>
</web-beans>

Now the Web Beans container will identify and install all components annotated @Component, @Standard or @Mock at deployment time.

(Note that a component with no component type annotation will never be discovered by the Web Beans container; the container searches for classes with component type annotations when scanning the classpath to discover components.)

It's interesting to compare this facility to today's popular container architectures. Various lightweight containers also allow conditional deployment of components that exist in the classpath, but the components that are to be deployed must be explicity, individually, listed in configuration code or in some XML configuration file. Web Beans does support component definition and configuration via XML, but in the common case where no complex configuration is required, component types allow a whole set of components to be enabled with a single line of XML. Meanwhile, a developer browsing the code can easily identify what context the component will be used in.

Component type precedence

If you've been paying attention, you're probably wondering how the container decides which implementation - PaymentProcessor or MockPaymentProcessor - to choose. Consider what happens when the container encounters this injection point:

@In PaymentProcessor pp;

There are now two components which satisfy the PaymentProcessor contract. Of course, we can't use a binding annotation to disambiguate, since binding annotations are hardcoded into the source at the injection point, and we want the container to be able to decide at deployment time!

The solution to this problem is that each component type has a different /precedence/. The precedence of the component types is determined by the order in which they appear in web-beans.xml. In our example, @Mock appears later than @Component so it has a higher precedence.

Whenever the container discovers that more than one component could satisfy the contract (API type plus binding annotations) specified by an injection point, it considers the relative precedence of the components. If one has a higher precedence than the others, it chooses the higher precedence component to inject. So, in our example, the container will inject MockPaymentProcessor when executing in the integration testing environment, and PaymentProcessor when executing in production (which is exactly what we want).

Example component types

Component types are useful for all kinds of things, some more examples:

  • @Mock and @Staging component types for testing
  • @AustralianTaxLaw for site-specific components
  • @SeamFramework, @Guice for third-party frameworks which build on Web Beans
  • @Standard for standard components defined by the Web Beans specification

I'm sure you can think of more applications...

Two more rules to remember?

As we've seen, the typesafe resolution algorith considers:

  1. API type
  2. binding annotations
  3. component type precedence

when resolving a component to be injected. If these rules fail to produce a unique component (if there are multiple components which all have the same high precedence, implement the required API type and support the required binding annotations), an exception will be thrown by the container at system initialization time.

The Web Beans group has discussed the possibility of introducing two additional rules to be used only when the rules above fail to narrow the list of components to a unique component. On balance, I'm in favor of these rules, since I think they're useful. However, the group as a whole is concerned that the additional complexity is confusing to developers. Therefore, we're seeking feedback from the community on the following ideas.

/Note:/ If you're not used to thinking in terms of binding annotations and component precendence, you're probably thinking that component resolution is already complex enough! But I'm confident that once you get used to it, the resolution algorithm is extremely robust and will grow to become totally intuitive.

Rule 1: The /least-derived implementation/ rule

This proposed rule states that if the algorithm above fails to result in a unique component, and if there is a /least-derived/ component in the set of candidates - one candidate that the other candidates all (directly or indirectly) extend, then the least-derived component will be chosen.

This rule is designed to ensure that it is easy to introduce new components into the system using implementation inheritance from pre-existing components, without breaking or changing the behavior of pre-existing clients.

For example, if I have this pre-existing component:

@Component @PayByCheque
ChequePaymentProcessor implements PaymentProcessor { ... }

and I introduce a new component that extends this one:

@Component @PayByCheque
AsynchronousChequePaymentProcessor 
    extends ChequePaymentProcessor 
    implements AsynchronousPaymentProcessor { ... }

Then the following injection point would continue to resolve to the base ChequePaymentProcessor component:

@In @PayByCheque PaymentProcessor processor;

While this new injection point would of course receive an instance of AsynchronousChequePaymentProcessor:

@In @PayByCheque AsynchronousPaymentProcessor processor;

Rule 2: The /least-specific bindings/ rule

This proposed rule is analogous to the previous rule, but refers to binding annotations rather than API types. It could be adopted either in addition to, or as an alternative to, Rule 1. It states that if the algorithm above fails to result in a unique component, and if there is exactly one component in the set of candidates that has /exactly/ the same binding annotations specified at the injection point, then this least-specific component will be chosen.

For example, if I have this pre-existing component:

@Component @PayByCheque 
ChequePaymentProcessor implements PaymentProcessor { ... }

and I introduce a new component that also supports the @PayByCheque binding annotation:

@Component @PayByCheque @Asynchronous
AsynchronousChequePaymentProcessor 
    extends ChequePaymentProcessor { ... }

Then the following injection point would continue to resolve to the base ChequePaymentProcessor component:

@In @PayByCheque PaymentProcessor paymentProcessor;

While this new injection point would of course receive an instance of AsynchronousPaymentProcessor:

@In @PayByCheque @Asynchronous PaymentProcessor paymentProcessor;

Are you scared yet?

Phew, we started to get a bit esoteric there! Don't worry, this is as complex as it gets, and if we get too much negative feedback about the two proposed rules above, it won't even get /that/ complex.

We're now really close to releasing a Community Review Draft for Web Beans. The purpose of the draft is to gather feedback on the component model, dependency management model and extensible context model that we've defined, and hopefully get people excited about Web Beans. We also need to get our work in front of the other EE6-related expert groups, so that they can start thinking about how they can possibly re-use and integrate with some of the mechanisms we have defined. However, the specification is by nature written in highly technical language, so this blog entry is the first in a serious of articles giving a friendly, introductory guide to Web Beans. When the Community Review Draft is released, please take the time to download and review it. But please read this series /first/.

A little history

First, some background. Web Beans was initiated by JBoss to help fill a gap in Java EE 5. The EE 5 platform has strong support for access to transactional resources via mature technologies including EJB3, JTA, JCA and JPA. Of course, the platform also features various widely-used web presentation technologies such as Java Servlets, JSP and JSF. However, the /web tier/ and /transactional tier/ have evolved independently and have missed the opportunity to develop a shared component model for web applications which provide access to transactional enterprise resources. Today, Web Beans is being driven by representatives of JBoss, Sun, Oracle and Google, along with several individual members. The component model is deeply influenced by Google Guice and Seam.

A unified component model for Java EE

Web Beans is a component model that is compatible with technologies in both tiers. Web Beans integrates with both JSF and EJB3, allowing an EJB3 session bean to act as a JSF managed bean, thus unifying the two component models. Additionally, Web Beans provides a /conversation model/ and /persistence context management/, thereby solving state management problems and optimistic transaction management problems that affect JSF and JPA. In sum, Web Beans makes it /much/ easier to build Java EE web applications which access the database via JPA.

While Web Beans provides a sweet spot for the integration of JSF and EJB3, the component model is much more generally useful. In particular, it supports use without either JSF or EJB3. An early question that arose was to what extent Web Beans would be limited to the EE and EJB3 environment. The unanimous decision of the group was that:

  1. A Web Bean does not /have/ to be an EJB
  2. Web Beans should be executable outside the EE environment

The first decision merely recognizes the fact that not every component needs the services that EJB provides (transaction demarcation, authorization, etc). However, Web Beans will not duplicate this functionality, so when these services are needed, the Web Bean should be written as a session bean.

The second decision allows components to be integration/unit testable outside the application server environment, and allows reuse of code in, for example, a batch process.

Some members, notably Bob Lee, argue that the work we've done is just as useful outside of the EE platform and that the component model in particular should be considered for use in Java SE. However, as spec lead, and in view of the language of our JSR proposal, I've made the decision to clearly specify the target environment as Java EE, and limit our discussions to what is needed by EE developers.

If, in future, there was pressure from the community and the JCP to open up some parts of Web Beans (for example, the sophisticated Guice-style dependency injection engine), we could at that time follow the precedent established by JPA in the EJB3 expert group and define behavior outside of the EE platform.

Web Bean components

So what, /exactly/, is a Web Bean?

A Web Bean is an application component containing 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 Beans container. Most Web Beans are /stateful/ and /contextual/. The lifecycle of a Web Bean is always managed by the container.

Let's back up a second. What does it 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), different clients of a component see the component in different states. The client-visible state depends upon which instance of the component the client has a reference to.

However, like a stateless or singleton model, and like JSF, but /unlike/ stateful session beans, the client does not control the lifecycle of the instance by explicitly creating and destroying it. Instead, a /context/ defines:

  • the lifecycle of an instance
  • the scope of visibility of this instance to clients

So clients (for example, other Web Beans) executing in the same /scope/ will see the same instance. But clients in a different scope will see a different instance.

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

  • they interact via well-defined public APIs
  • their lifecycles are completely decoupled

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

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

A Web Bean component comprises:
  • A component type
  • Either a bean implementation class or a resolver method
  • A set of API types
  • A (possibly empty) set of binding annotation types
  • A scope
  • A component name

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

Component types

All we need to know about /component types/ for now is that a Web Beans developer may define some kind of stereotype as an annotation, for example @Mock, @Staging or @AustralianTaxLaw that allows whole sets of components to be conditionally installed in particular deployments of the system. We'll talk more about this unique and powerful feature in a later installment.

A very simple Web Bean might just use the built-in component type @Component:

@Component
public class Credentials { ... }

The prescence of a component type annotation identifies this class as a Web Bean to the Web Beans container.

API types, binding annotation and dependency injection

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

  • An API
  • A (possibly empty) set of binding annotations

An API is a user-defined class or interface. (If the component is an EJB session bean, the API type is the @Local interface.) A /binding annotation/ is a user-defined annotation that is itself annotated @BindingType.

The container searches for a component which satisfies this contract (implements the API, and supports the binding annotations), and injects that component.

For example, if this was the injection point:

@In @CreditCard PaymentProcessor paymentProcessor;

The following component could be injected:

@CreditCard @Component
public class CreditCardPaymentProcessor 
    implements PaymentProcessor { ... }

Web Beans defines a sophisticated but intuitive /resolution algorithm/ that helps the container decide what to do if there is more than one component that satisfies a particular contract. We'll get into the details in a later installment.

Component scope

The /scope/ defines the lifecycle and visibility of instances of the component. The Web Beans context model is extensible, accommodating arbitrary scopes. However, certain important scopes are built-in to the specification, and provided by the Web Beans container. For example, any web application has access to a /session/ scope:

@SessionScoped @Component
public class ShoppingCart { ... }

We'll talk more about scopes in a later installment.

Component names and Unified EL

All Web Beans may be used by /name/ in Unified EL expressions. It is easy to customize the name of a Web Bean:

@SessionScoped @Component @Named("cart")
public class ShoppingCart { ... }

Then we can easily use the component in a JSF page:

<h:dataTable value="#{cart.lineItems}" var="item">
    ....
</h:dataTable>

Resolver methods and web-beans.xml

Most Web Beans are defined by writing an implementation class and annotating it. However, there are two extra ways to define a Web Bean:

  1. via an XML deployment descriptor named web-beans.xml
  2. by writing a resolver method

We'll cover web-beans.xml in a future installment.

A /resolver method/ is a method that is called by the container to obtain an instance of the component when no instance exists in the current context. For example:

@SessionScoped @Component
public class Login {

    User user;
    ...
    
    public void login() {
        user = ...;
    }
    
    @Resolves @LoggedIn User getCurrentUser() {
        return user;
    }

}

A resolver method is a first-class Web Beans component. Once again, we'll talk more about resolver method in a future installment.

Logging in

Let's illustrate these ideas by fleshing out the previous example. We're going to implement user login/logout. First, we'll define a component to hold the username and password entered during login:

@Component
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 component is bound to the login prompt in the following JSF form:

<f:form>
    <h:panelGrid columns="2" rendered="#{!login.isLoggedIn}">
        <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.isLoggedIn}"/>
    <h:commandButton value="Logout" acion="#{login.logout}" rendered="#{login.isLoggedIn}"/>
</f:form>

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

@SessionScoped @Component
public class Login {

    @In Credentials credentials;
    @In @UserDatabase 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;
    }
    
    @Resolves @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 component can easily inject the current user:

@Component
public class DocumentEditor {

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

Stay with me, folks!

Hopefully, this gives a flavor of the Web Beans component model. There's lots more to talk about, I hope you'll find the time to follow along with the rest of the series.

Showing 1026 to 1030 of 1200 blog entries