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:
- A Web Bean does not /have/ to be an EJB
- 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:
- via an XML deployment descriptor named web-beans.xml
- 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.