Introduction To Web Beans
Introduction To Web Beans
The new Java standard for dependency injection and contextual state management
Gavin King
Web Beans (JSR-299) specification lead
Red Hat Middleware LLC
Table of Contents
I. Using contextual objects .................................................................................................................... 1 1. Getting started with Web Beans ............................................................................................... 3 1.1. Your first Web Bean ...................................................................................................... 3 1.2. What is a Web Bean? ..................................................................................................... 4 1.2.1. API types, binding types and dependency injection ................................................... 5 1.2.2. Deployment types ............................................................................................... 5 1.2.3. Scope ............................................................................................................... 6 1.2.4. Web Bean names and Unified EL .......................................................................... 6 1.2.5. Interceptor binding types ...................................................................................... 6 1.3. What kinds of objects can be Web Beans? .......................................................................... 7 1.3.1. Simple Web Beans .............................................................................................. 7 1.3.2. Enterprise Web Beans .......................................................................................... 7 1.3.3. Producer methods ............................................................................................... 8 1.3.4. JMS endpoints ................................................................................................... 8 2. JSF web application example ................................................................................................... 9 3. Dependency injection .............................................................................................................11 3.1. Binding annotations ......................................................................................................12 3.1.1. Binding annotations with members ........................................................................12 3.1.2. Combinations of binding annnotations ...................................................................13 3.1.3. Binding annotations and producer methods .............................................................13 3.1.4. The default binding type .....................................................................................13 3.2. Deployment types ........................................................................................................13 3.2.1. Enabling deployment types ..................................................................................14 3.2.2. Deployment type precedence ...............................................................................14 3.2.3. Example deployment types ..................................................................................15 3.3. Fixing unsatisfied dependencies ......................................................................................15 3.4. Client proxies ..............................................................................................................15 3.5. Obtaining a Web Bean by programatic lookup ...................................................................16 3.6. Lifecycle callbacks, @Resource, @EJB and @PersistenceContext ........................................16 4. Scopes and contexts ...............................................................................................................17 4.1. Scope types .................................................................................................................17 4.2. Built-in scopes .............................................................................................................17 4.3. The conversation scope .................................................................................................18 4.3.1. Conversation demarcation ...................................................................................18 4.3.2. Conversation propagation ....................................................................................19 4.3.3. Conversation timeout ..........................................................................................19 4.4. The dependent pseudo-scope ..........................................................................................19 4.4.1. The @New annotation ........................................................................................19 5. Producer methods .................................................................................................................21 5.1. Scope of a producer method ...........................................................................................21 5.2. Injection into producer methods ......................................................................................22 5.3. Use of @New with producer methods ..............................................................................22 II. Developing loosely-coupled code ......................................................................................................23 6. Interceptors ..........................................................................................................................24 6.1. Interceptor bindings ......................................................................................................24 6.2. Implementing interceptors ..............................................................................................24 6.3. Enabling interceptors ....................................................................................................25 6.4. Interceptor bindings with members ..................................................................................25 6.5. Multiple interceptor binding annotations ...........................................................................26 6.6. Interceptor binding type inheritance .................................................................................26 6.7. Use of @Interceptors ....................................................................................................26 7. Decorators ............................................................................................................................28 7.1. Delegate attributes ........................................................................................................28 7.2. Enabling decorators ......................................................................................................29 8. Events ..................................................................................................................................30 8.1. Event observers ...........................................................................................................30 8.2. Event producers ...........................................................................................................30
ii
Introduction to Web Beans 8.3. Registering observers dynamically ...................................................................................31 8.4. Event bindings with members .........................................................................................31 8.5. Multiple event bindings .................................................................................................32 8.6. Transactional observers .................................................................................................32 III. Making the most of strong typing .....................................................................................................34 9. Stereotypes ...........................................................................................................................35 9.1. Default scope and deployment type for a stereotype ............................................................35 9.2. Restricting scope and type with a stereotype ......................................................................35 9.3. Interceptor bindings for stereotypes .................................................................................36 9.4. Name defaulting with stereotypes ....................................................................................36 9.5. Standard stereotypes .....................................................................................................36 10. Specialization ......................................................................................................................37 10.1. Using specialization ....................................................................................................37 10.2. Advantages of specialization .........................................................................................38 11. Defining Web Beans using XML ............................................................................................39 11.1. Declaring Web Bean classes .........................................................................................39 11.2. Declaring Web Bean metadata ......................................................................................39 11.3. Declaring Web Bean members ......................................................................................40 11.4. Declaring inline Web Beans .........................................................................................40 11.5. Using a schema ..........................................................................................................40 IV. Web Beans and the Java EE ecosystem .............................................................................................42 12. Java EE integration .............................................................................................................43 12.1. Injecting Java EE resources into a Web Bean ...................................................................43 12.2. Calling a Web Bean from a Servlet ................................................................................43 12.3. Calling a Web Bean from a Message-Driven Bean ............................................................43 12.4. JMS endpoints ...........................................................................................................44 13. Extending Web Beans ..........................................................................................................45 13.1. The Manager object ....................................................................................................45 13.2. The Bean class ...........................................................................................................46 13.3. The Context interface ..................................................................................................46 14. Next steps ...................................................................................................................................47
iii
Dependency injection, together with contextual lifecycle management, saves the user of an unfamiliar API from having to ask and answer the following questions: what is the lifecycle of this object? how many simultaneous clients can it have? is it multithreaded? where can I get one from? do I need to explicitly destroy it? where should I keep my reference to it when I'm not using it directly? how can I add an indirection layer, so that the implementation of this object can vary at deployment time? how should I go about sharing this object between other objects?
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: event notifications decouple event producers from event consumers, interceptors decouple technical concerns from business logic, and decorators allow business concerns to be compartmentalized.
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: all JavaBeans, all EJBs, and all Servlets.
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.
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) { ... } }
Unfortunately, we don't have a pre-existing 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 in 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 availableif the SentenceTranslator EJB was not deployedthe Web Bean manager would throw an UnsatisfiedDependencyException. If more than one implementation of Translator was available, the Web Bean manager would throw an AmbiguousDependencyException.
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: they interact via well-defined public APIs their lifecycles are completely decoupled
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 3.2, Deployment types. Note that not all clients of a Web Bean are Web Beans. Other objects such as Servlets or Message-Driven Beanswhich are by nature not injectable, contextual objectsmay 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.
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 therefore be injected to the example injection point:
@CreditCard public class CreditCardPaymentProcessor implements PaymentProcessor { ... } PaymentProcessor.
It could
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 3, Dependency injection.
@Mock public class MockSentenceTranslator implements Translator { public String translate(String sentence) { return "Lorem ipsum dolor sit amet"; } }
We would enable the deployment type @Mock in our testing environment, to indicate that any other Web Bean annotated @Mock should be used. We'll talk more about this unique and powerful feature in Section 3.2, Deployment types.
MockSentenceTranslator
and
1.2.3. Scope
The scope defines the lifecycle and visibility of instances of the Web Bean. 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 Bean manager. A scope is represented by an annotation type. For example, any web application may have session scoped Web Beans:
@SessionScoped public class ShoppingCart { ... }
An instance of a session scoped Web Bean is bound to a user session and is shared by all requests that execute in the context of that session. By default, Web Beans belong to a special scope called the dependent pseudo-scope. Web Beans with this scope are pure dependent objects of the object into which they are injected, and their lifecycle is bound to the lifecycle of that object. We'll talk more about scopes in Chapter 4, Scopes and contexts.
Now we can easily use the Web Bean in any JSF or JSP page:
<h:dataTable value="#{cart.lineItems}" var="item"> .... </h:dataTable>
It's even easier to just let the name be defaulted by the Web Bean manager:
@SessionScoped @Named public class ShoppingCart { ... }
shoppingCartthe
However, it is more elegant, and better practice, to indirect the interceptor binding through an interceptor binding type:
@SessionScoped @Transactional public class ShoppingCart { ... }
We'll discuss Web Beans interceptors and decorators in Chapter 6, Interceptors and Chapter 7, Decorators.
Thus, almost every JavaBean is a simple Web Bean. Every interface implemented directly or indirectly by a simple Web Bean is an API type of the simple Web Bean. The class and its superclasses are also API types.
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: method-level transaction management and security, concurrency management, instance-level passivation for stateful session beans and instance-pooling for stateless session beans, remote and web service invocation, and
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: @Stateful or @Singleton.
@Stateless,
The result of a producer method is injected just like any other Web Bean.
@Random int randomNumber
The method return type and all interfaces it extends/implements directly or indirectly are API types of the producer method. If the return type is a class, all superclasses are also API types. Some producer methods return objects that require explicit destruction:
@Produces @RequestScoped Connection connect(User user) { return createConnection( user.getId(), user.getPassword() ); }
This disposal method is called automatically by the Web Bean manager at the end of the request. We'll talk much more about producer methods in Chapter 5, Producer methods.
This Web Bean is bound to the login prompt in the following JSF form:
<f: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}"/> </f: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; } }
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.
10
Dependency injection always occurs when the Web Bean instance is first instantiated. First, the Web Bean manager calls the Web Bean constructor, to obtain an instance of the Web Bean. Next, the Web Bean manager initializes the values of all injected fields of the Web Bean. Next, the Web Bean manager calls all initializer methods of Web Bean. Finally, the @PostConstruct method of the Web Bean, if any, is called.
Constructor parameter injection is not supported for EJB beans, since the EJB is instantiated by the EJB container, not the Web Bean manager. Parameters of constructors and initializer methods need not be explicitly annotated when the default binding type @Current applies. Injected fields, however, must specify a binding type, even when the default binding type applies. If the field does not specify a binding type, it will not be injected. Producer methods also support parameter injection:
@Produces Checkout createCheckout(ShoppingCart cart) { return new Checkout(cart); }
Finally, observer methods (which we'll meet in Chapter 8, Events), disposal methods and destructor methods all support parameter injection. The Web Beans specification defines a procedure, called the typesafe resolution algorithm, that the Web Bean manager follows when identifying the Web Bean 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 manager will inform the user immediately if a Web Bean's dependencies cannot be satisfied, by throwing a UnsatisfiedDependencyException or AmbiguousDependencyException.
11
Dependency injection
The purpose of this algorithm is to allow multiple Web Beans to implement the same API type and either: allow the client to select which implementation it requires using binding annotations, allow the application deployer to select which implementation is appropriate for a particular deployment, without changes to the client, by enabling or disabling deployment types, or allow one implementation of an API to override another implementation of the same API at deployment time, without changes to the client, using deployment type precedence.
Let's explore how the Web Beans manager determines a Web Bean to be injected.
A client Web Bean developer uses the binding annotation to specify exactly which Web Bean should be injected. Using field injection:
@PayByCheque PaymentProcessor chequePaymentProcessor; @PayByCreditCard PaymentProcessor creditCardPaymentProcessor;
12
Dependency injection
You can tell the Web Bean manager to ignore a member of a binding annotation type by annotating the member @NonBinding.
In this case, only a Web Bean which has both binding annotations would be eligible for injection.
Suppose we had some Web Bean that interacted with an external system to process payments:
public class ExternalPaymentProcessor { public void process(Payment p) { ...
13
Dependency injection
} }
Since this Web Bean does not explicitly specify a deployment type, it has the default deployment type @Production. For integration or unit testing, the external system is slow or unavailable. So we would create a mock object:
@Mock public class MockPaymentProcessor implements PaymentProcessor { @Override public void process(Payment p) { p.setSuccessful(true); } }
But how does the Web Bean manager determine which implementation to use in a particular deployment?
Now the Web Bean manager will identify and install all Web Beans annotated ployment time.
@Production, @Standard
or
@Mock
at de-
The deployment type @Standard is used only for certain special Web Beans defined by the Web Beans specification. We can't use it for our own Web Beans, and we can't disable it. The deployment type @Production is the default deployment type for Web Beans which don't explicitly declare a deployment type, and may be disabled.
There are now two Web Beans which satisfy the PaymentProcessor contract. Of course, we can't use a binding annotation to disambiguate, since binding annotations are hard-coded into the source at the injection point, and we want the manager to be able to decide at deployment time! The solution to this problem is that each deployment type has a different precedence. The precedence of the deployment types is determined by the order in which they appear in web-beans.xml. In our example, @Mock appears later than @Production so it has a higher precedence. Whenever the manager discovers that more than one Web Bean could satisfy the contract (API type plus binding annotations) specified by an injection point, it considers the relative precedence of the Web Beans. If one has a higher precedence than the others, it chooses the higher precedence Web Bean to inject. So, in our example, the Web Bean manager will inject MockPaymentProcessor when executing in our integration testing environment (which is exactly what we want).
14
Dependency injection
It's interesting to compare this facility to today's popular manager architectures. Various "lightweight" containers also allow conditional deployment of classes that exist in the classpath, but the classes that are to be deployed must be explicity, individually, listed in configuration code or in some XML configuration file. Web Beans does support Web Bean definition and configuration via XML, but in the common case where no complex configuration is required, deployment types allow a whole set of Web Beans to be enabled with a single line of XML. Meanwhile, a developer browsing the code can easily identify what deployment scenarios the Web Bean will be used in.
and @Staging deployment types for testing for site-specific Web Beans for third-party frameworks which build on Web Beans
@AustralianTaxLaw
15
Dependency injection
The following Java types cannot be proxied by the Web Bean manager: classes which are declared final or have a final method, classes which have no non-private constructor with no parameters, and arrays and primitive types.
It's usually very easy to fix an UnproxyableDependencyException. Simply add a constructor with no parameters to the injected class, introduce an interface, or change the scope of the injected Web Bean to @Dependent.
The Manager object provides a set of methods for obtaining a Web Bean instance programatically.
PaymentProcessor p = manager.getInstanceByType(PaymentProcessor.class);
Binding annotations may be specified by subclassing the helper class AnnotationLiteral, since it is otherwise difficult to instantiate an annotation type in Java.
PaymentProcessor p = manager.getInstanceByType(PaymentProcessor.class, new AnnotationLiteral<CreditCard>(){});
If the binding type has an annotation member, we can't use an anonymous subclass of AnnotationLiteralinstead we'll need to create a named subclass:
abstract class CreditCardBinding extends AnnotationLiteral<CreditCard> implements CreditCard {} PaymentProcessor p = manager.getInstanceByType(PaymentProcessor.class, new CreditCardBinding() { public void value() { return paymentType; } } );
16
For example, if we have a session scoped Web Bean, CurrentUser, all Web Beans 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.
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 Web Bean:
@ClusterScoped public class SecondLevelCache { ... }
For a web application that uses Web Beans: any servlet request has access to active request, session and application scopes, and, additionally any JSF request has access to an active conversation scope.
The request and application scopes are also active: during invocations of EJB remote methods, during EJB timeouts,
17
during message delivery to a message-driven bean, and during web service invocations.
ContextNotActiveEx-
If the application tries to invoke a Web Bean with a scope that does not have an active context, a ception is thrown by the Web Bean manager at runtime.
Three of the four built-in scopes should be extremely familiar to every Java EE developer, so let's not waste time discussing them here. One of the scopes, however, is new.
A conversation represents a task, a unit of work from the point of view of the user. The conversation context holds state associated with what the user is currently working on. If the user is doing multiple things at the same time, there are multiple conversations. The conversation context is active during any JSF request. However, most conversations are destroyed at the end of the request. If a conversation should hold state across multiple requests, it must be explicitly promoted to a long-running conversation.
To promote the conversation associated with the current request to a long-running conversation, call the begin() method from application code. To schedule the current long-running conversation context for destruction at the end of the current request, call end(). In the following example, a conversation-scoped Web Bean controls the conversation with which it is associated:
@ConversationScoped @Stateful public class OrderBuilder { private Order order; private @Current Conversation conversation; private @PersistenceContext(type=EXTENDED) EntityManager em; @Produces public Order getOrder() { return order; } public Order createOrder() { order = new Order(); conversation.begin(); return order; } public void addLineItem(Product product, int quantity) { order.add( new LineItem(product, quantity) ); } public void saveOrder(Order order) { em.persist(order); conversation.end(); } @Remove public void destroy() {} }
18
This Web Bean is able to control its own lifecycle through use of the Conversation API. But some other Web Beans have a lifecycle which depends completely upon another object.
The Web Bean manager is also required to propagate conversations across any redirect, even if the conversation is not marked long-running. This makes it very easy to implement the common POST-then-redirect pattern, without resort to fragile constructs such as a "flash" object. In this case, the Web Bean manager automatically adds a request parameter to the redirect URL.
When an injection point of a Web Bean resolves to a dependent Web Bean, a new instance of the dependent Web Bean is created every time the first Web Bean is instantiated. Instances of dependent Web Beans are never shared between different Web Beans or different injection points. They are dependent objects of some other Web Bean instance. Dependent Web Bean instances are destroyed when the instance they depend upon is destroyed. Web Beans makes it easy to obtain a dependent instance of a Java class or EJB bean, even if the class or EJB bean is already declared as a Web Bean with some other scope type.
@Dependent,
binding type
@New,
API type
Calculator,
implementation class
Calculator
19
Scopes and contexts and deployment type @Standard is implicitly defined. This is true even if Calculator is already declared with a different scope type, for example:
@ConversationScoped public class Calculator { ... }
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 producer methods, as we'll see in the next chapter.
20
For example, producer methods let us: expose a JPA entity as a Web Bean, expose any JDK class as a Web Bean, define multiple Web Beans, with different scopes or initialization, for the same implementation class, or vary the implementation of an API type at runtime.
In particular, producer methods let us use runtime polymorphism with Web Beans. As we've seen, deployment types are a powerful solution to the problem of deployment-time polymorphism. But once the system is deployed, the Web Bean implementation is fixed. A producer method has no such limitation:
@SessionScoped public class Preferences { private PaymentStrategyType paymentStrategy; ... @Produces @Preferred public PaymentStrategy getPaymentStrategy() { switch (paymentStrategy) { case CREDIT_CARD: return new CreditCardPaymentStrategy(); case CHEQUE: return new ChequePaymentStrategy(); case PAYPAL: return new PayPalPaymentStrategy(); default: return null; } } }
This injection point has the same type and binding annotations as the producer method, so it resolves to the producer method using the usual Web Beans injection rules. The producer method will be called by the Web Bean manager to obtain an instance to service this injection point.
21
Producer methods
Now, when the producer method is called, the returned PaymentStrategy will be bound to the session context. The producer method won't be called again in the same session.
Wait, what if CreditCardPaymentStrategy is a request scoped Web Bean? Then the producer method has the effect of "promoting" the current request scoped instance into session scope. This is almost certainly a bug! The request scoped object will be destroyed by the Web Bean manager before the session ends, but the reference to the object will be left "hanging" in the session scope. This error will not be detected by the Web Bean manager, so please take extra care when returning Web Bean instances from producer methods! There's at least three ways we could go about fixing this bug. We could change the scope of the CreditCardPaymentStrategy implementation, but this would affect other clients of that Web Bean. A better option would be to change the scope of the producer method to @Dependent or @RequestScoped. But a more common solution is to use the special @New binding annotation.
Then a new dependent instance of CreditCardPaymentStrategy will be created, passed to the producer method, returned by the producer method and finally bound to the session context. The dependent object won't be destroyed until the Preferences object is destroyed, at the end of the session.
22
These techniques serve to enable loose coupling of client and server. The client is no longer tightly bound to an implementation of an API, nor is it required to manage the lifecycle of the server object. This approach lets stateful objects interact as if they were services. Loose coupling makes a system more dynamic. The system can respond to change in a well-defined manner. In the past, frameworks that attempted to provide the facilities listed above invariably did it by sacrificing type safety. Web Beans is the first technology that achieves this level of loose coupling in a typesafe way. Web Beans provides three extra important facilities that further the goal of loose coupling: interceptors decouple technical concerns from business logic, decorators may be used to decouple some business concerns, and event notifications decouple event producers from event consumers.
Chapter 6. Interceptors
Web Beans re-uses the basic interceptor architecture of EJB 3.0, extending the functionality in two directions: Any Web Bean may have interceptors, not just session beans. Web Beans features a more sophisticated annotation-based approach to binding interceptors to Web Beans.
The EJB specification defines two kinds of interception points: business method interception, and lifecycle callback interception.
A business method interceptor applies to invocations of methods of the Web Bean by clients of the Web Bean:
public class TransactionInterceptor { @AroundInvoke public Object manageTransaction(InvocationContext ctx) { ... } }
An interceptor class may intercept both lifecycle callbacks and business methods.
All Web Beans interceptors are simple Web Beans, and can take advantage of dependency injection and contextual lifecycle management.
@ApplicationScoped @Transactional @Interceptor
24
Interceptors
public class TransactionInterceptor { @Resource Transaction transaction; @AroundInvoke public Object manageTransaction(InvocationContext ctx) { ... } }
Whoah! Why the angle bracket stew? Well, the XML declaration solves two problems: it enables us to specify a total ordering for all the interceptors in our system, ensuring deterministic behavior, and it lets us enable or disable interceptor classes at deployment time.
For example, we could specify that our security interceptor runs before our TransactionInterceptor.
<Interceptors> <sx:SecurityInterceptor/> <tx:TransactionInterceptor/> </Interceptors>
Web Beans will use the value of requiresNew to choose between two different interceptors, TransactionInterceptor and RequiresNewTransactionInterceptor.
@Transactional(requiresNew=true) @Interceptor public class RequiresNewTransactionInterceptor { @AroundInvoke public Object manageTransaction(InvocationContext ctx) { ... } }
But what if we only have one interceptor and we want the manager to ignore the value of requiresNew when binding interceptors? We can use the @NonBinding annotation:
@InterceptorBindingType @Target({METHOD, TYPE}) @Retention(RUNTIME) public @interface Secure { @NonBinding String[] rolesAllowed() default {};
25
Interceptors
However, in very complex cases, an interceptor itself may specify some combination of interceptor binding types:
@Transactional @Secure @Interceptor public class TransactionalSecureInterceptor { ... }
Then this interceptor could be bound to the checkout() method using any one of the following combinations:
public class ShoppingCart { @Transactional @Secure public void checkout() { ... } } @Secure public class ShoppingCart { @Transactional public void checkout() { ... } } @Transactionl public class ShoppingCart { @Secure public void checkout() { ... } } @Transactional @Secure public class ShoppingCart { public void checkout() { ... } }
Well, fortunately, Web Beans works around this missing feature of Java. We may annotate one interceptor binding type with other interceptor binding types. The interceptor bindings are transitiveany Web Bean with the first interceptor binding inherits the interceptor bindings declared as meta-annotations.
@Transactional @Secure @InterceptorBindingType @Target(TYPE) @Retention(RUNTIME) public @interface Action { ... }
Any Web Bean annotated @Action will be bound to both TransactionInterceptor and SecurityInterceptor. (And even TransactionalSecureInterceptor, if it exists.)
26
Interceptors
However, this approach suffers the following drawbacks: the interceptor implementation is hardcoded in business code, interceptors may not be easily disabled at deployment time, and the interceptor ordering is non-globalit is determined by the order in which interceptors are listed at the class level.
27
Chapter 7. Decorators
Interceptors are a powerful way to capture and separate concerns which are orthogonal to the type system. Any interceptor is able to intercept invocations of any Java type. This makes them perfect for solving technical concerns such as transaction management and security. However, by nature, interceptors are unaware of the actual semantics of the events they intercept. Thus, interceptors aren't an appropriate tool for separating business-related concerns. The reverse is true of decorators. A decorator intercepts invocations only for a certain Java interface, and is therefore aware of all the semantics attached to that interface. This makes decorators a perfect tool for modeling some kinds of business concerns. It also means that a decorator doesn't have the generality of an interceptor. Decorators aren't able to solve technical concerns that cut across many disparate types. Suppose we have an interface that represents accounts:
public interface Account { public BigDecimal getBalance(); public User getOwner(); public void withdraw(BigDecimal amount); public void deposit(BigDecimal amount); }
Several different Web Beans in our system implement the Account interface. However, we have a common legal requirement that, for any kind of account, large transactions must be recorded by the system in a special log. This is a perfect job for a decorator. A decorator is a simple Web Bean that implements the type it decorates and is annotated @Decorator.
@Decorator public abstract class LargeTransactionDecorator implements Account { @Decorates Account account; @PersistenceContext EntityManager em; public void withdraw(BigDecimal amount) { account.withdraw(amount); if ( amount.compareTo(LARGE_AMOUNT)>0 ) { em.persist( new LoggedWithdrawl(amount) ); } } public void deposit(BigDecimal amount); account.deposit(amount); if ( amount.compareTo(LARGE_AMOUNT)>0 ) { em.persist( new LoggedDeposit(amount) ); } } }
Unlike other simple Web Beans, a decorator may be an abstract class. If there's nothing special the decorator needs to do for a particular method of the decorated interface, you don't need to implement that method.
A delegate attribute may specify a binding annotation. Then the decorator will only be bound to Web Beans with the same binding.
@Decorates @Foreign Account account;
28
Decorators
A decorator is bound to any Web Bean which: has the type of the delegate attribute as an API type, and has all binding types that are declared by the delegate attribute.
InvocationCon-
The decorator may invoke the delegate attribute, which has much the same effect as calling text.proceed() from an interceptor.
This declaration serves the same purpose for decorators that the <Interceptors> declaration serves for interceptors: it enables us to specify a total ordering for all decorators in our system, ensuring deterministic behavior, and it lets us enable or disable decorator classes at deployment time.
Interceptors for a method are called before decorators that apply to that method.
29
Chapter 8. Events
The Web Beans event notification facility allows Web Beans to interact in a totally decoupled manner. Event producers raise events that are then delivered to event observers by the Web Bean manager. This basic schema might sound like the familiar observer/observable pattern, but there are a couple of twists: not only are event producers decoupled from observers; observers are completely decoupled from producers, observers can specify a combination of "selectors" to narrow the set of event notifications they will receive, and observers can be notified immediately, or can specify that delivery of the event should be delayed until the end of the current transaction
The annotated parameter is called the event parameter. The type of the event parameter is the observed event type. Observer methods may also specify "selectors", which are just instances of Web Beans binding types. When a binding type is used as an event selector, it is called an event binding type.
@BindingType @Target({PARAMETER, FIELD}) @Retention(RUNTIME) public @interface Updated { ... }
We specify the event bindings of the observer method by annotating the event parameter:
public void afterDocumentUpdate(@Observes @Updated Document document) { ... }
An observer method need not specify any event bindingsin this case it is interested in all events of a particular type. If it does specify event bindings, it is only interested in events which also have those event bindings. The observer method may have additional parameters, which are injected according to the usual Web Beans method parameter injection semantics:
public void afterDocumentUpdate(@Observes @Updated Document document, User user) { ... }
The @Observable annotation implicitly defines a Web Bean with scope @Dependent and deployment type @Standard, with an implementation provided by the Web Bean manager. A producer raises events by calling the fire() method of the Event interface, passing an event object:
documentEvent.fire(document);
An event object may be an instance of any Java class that has no type variables or wildcard type parameters. The event will be delivered to every observer method that: has an event parameter to which the event object is assignable, and specifies no event bindings.
The Web Bean manager simply calls all the observer methods, passing the event object as the value of the event paramet30
Events
er. If any observer method throws an exception, the Web Bean manager stops calling observer methods, and the exception is rethrown by the fire() method. To specify a "selector", the event producer may pass an instance of the event binding type to the fire() method:
documentEvent.fire( document, new AnnotationLiteral<Updated>(){} );
AnnotationLiteral
makes it possible to instantiate binding types inline, since this is otherwise difficult
The event will be delivered to every observer method that: has an event parameter to which the event object is assignable, and does not specify any event binding except for the event bindings passed to fire().
Alternatively, event bindings may be specified by annotating the event notifier injection point:
@Observable @Updated Event<Document> documentUpdatedEvent
Then every event fired via this instance of Event has the annotated event binding. The event will be delivered to every observer method that: has an event parameter to which the event object is assignable, and does not specify any event binding except for the event bindings passed to the event notifier injection point.
fire()
Event binding types may be specified by the event notifier injection point or by passing event binding type instances to the observe() method:
documentEvent.observe( new Observer<Document>() { public void notify(Document doc) { ... } }, new AnnotationLiteral<Updated>(){} );
The member value is used to narrow the messages delivered to the observer:
public void adminLoggedIn(@Observes @Role(ADMIN) LoggedIn event) { ... }
Event binding type members may be specified statically by the event producer, via annotations at the event notifier injection point:
@Observable @Role(ADMIN) Event<LoggedIn> LoggedInEvent;}}
Alternatively, the value of the event binding type member may be determined dynamically by the event producer. We start 31
Events
When this event occurs, all of the following observer methods will be notified:
public void afterBlogUpdate(@Observes @Updated @Blog Document document) { ... } public void afterDocumentUpdate(@Observes @Updated Document document) { ... } public void onAnyBlogEvent(@Observes @Blog Document document) { ... } public void onAnyDocumentEvent(@Observes Document document) { ... }}}
There are three kinds of transactional observers: observers are called during the after completion phase of the transaction, but only if the transaction completes successfully
@AfterTransactionSuccess
observers are called during the after completion phase of the transaction, but only if the transaction fails to complete successfully
@AfterTransactionFailure @AfterTransactionCompletion
observers are called during the after completion phase of the transaction observers are called during the before completion phase of the transaction
@BeforeTransactionCompletion
Transactional observers are very important in a stateful object model like Web Beans, because state is often held for longer than a single atomic transaction. Imagine that we have cached a JPA query result set in the application scope:
@ApplicationScoped @Singleton public class Catalog { @PersistenceContext EntityManager em; List<Product> products; @Produces @Catalog List<Product> getCatalog() { if (products==null) { products = em.createQuery("select p from Product p where p.deleted = false")
32
Events
From time to time, a Product is created or deleted. When this occurs, we need to refresh the should wait until after the transaction completes successfully before performing this refresh! The Web Bean that creates and deletes Products could raise events, for example:
@Stateless public class ProductManager { @PersistenceContext EntityManager em; @Observable Event<Product> productEvent; public void delete(Product product) { em.delete(product); productEvent.fire(product, new AnnotationLiteral<Deleted>(){}); } public void persist(Product product) { em.persist(product); productEvent.fire(product, new AnnotationLiteral<Created>(){}); } ... }
Product
catalog. But we
And now Catalog can observe the events after successful completion of the transaction:
@ApplicationScoped @Singleton public class Catalog { ... void addProduct(@AfterTransactionSuccess @Observes @Created Product product) { products.add(product); } void addProduct(@AfterTransactionSuccess @Observes @Deleted Product product) { products.remove(product); } }
33
or
@Updated,
or
DocumentUpdatedEvent.
The annotations are reusable. They help describe common qualities of disparate parts of the system. They help us categorize and understand our code. They help us deal with common concerns in a common way. They make our code more literate and more understandable. Web Beans stereotypes take this idea a step further. A stereotype models a common role in your application architecture. It encapsulates various properties of the role, including scope, interceptor bindings, deployment type, etc, into a single reusable package. Even Web Beans XML metadata is strongly typed! There's no compiler for XML, so Web Beans takes advantage of XML schemas to validate the Java types and attributes that appear in XML. This approach turns out to make the XML more literate, just like annotations made our Java code more literate. We're now ready to meet some more advanced features of Web Beans. Bear in mind that these features exist to make our code both easier to validate and more understandable. Most of the time you don't ever really need to use these features, but if you use them wisely, you'll come to appreciate their power.
Chapter 9. Stereotypes
According to the Web Beans specification: In many systems, use of architectural patterns produces a set of recurring Web Bean roles. A stereotype allows a framework developer to identify such a role and declare some common metadata for Web Beans with that role in a central place. A stereotype encapsulates any combination of: a default deployment type, a default scope type, a restriction upon the Web Bean scope, a requirement that the Web Bean implement or extend a certain type, and a set of interceptor binding annotations.
A stereotype may also specify that all Web Beans with the stereotype have defaulted Web Bean names. A Web Bean may declare zero, one or multiple stereotypes. A stereotype is a Java annotation type. This stereotype identifies action classes in some MVC framework:
@Retention(RUNTIME) @Target(TYPE) @Stereotype public @interface Action {}
35
Stereotypes
If a particular action class attempts to specify a scope other than the Web Beans request scope, an exception will be thrown by the Web Bean manager at initialization time. We can also force all Web Bean with a certain stereotype to implement an interface or extend a class:
@Retention(RUNTIME) @Target(TYPE) @RequestScoped @WebTier @Stereotype(requiredTypes=AbstractAction.class) public @interface Action {}
If a particular action class does not extend the class AbstractAction, an exception will be thrown by the Web Bean manager at initialization time.
This helps us get technical concerns even further away from the business code!
This stereotype is intended for use with JSF. Instead of using JSF managed beans, just annotate a Web Bean @Model, and use it directly in your JSF page. 36
But in our staging environment, we override that implementation of PaymentProcessor with a different Web Bean:
@CreditCard @Stateless @Staging public class StagingCreditCardPaymentProcessor implements PaymentProcessor { ... }
What we've tried to do with StagingCreditCardPaymentProcessor is to completely replace AsyncPaymentProcessor in a particular deployment of the system. In that deployment, the deployment type @Staging would have a higher priority than the default deployment type @Production, and therefore clients with the following injection point:
@CreditCard PaymentProcessor ccpp
Would receive an instance of StagingCreditCardPaymentProcessor. Unfortunately, there are several traps we can easily fall into: the higher-priority Web Bean may not implement all the API types of the Web Bean that it attempts to override, the higher-priority Web Bean may not declare all the binding types of the Web Bean that it attempts to override, the higher-priority Web Bean might not have the same name as the Web Bean that it attempts to override, or the Web Bean that it attempts to override might declare a producer method, disposal method or observer method.
In each of these cases, the Web Bean that we tried to override could still be called at runtime. Therefore, overriding is somewhat prone to developer error. Web Beans provides a special feature, called specialization, that helps the developer avoid these traps. Specialization looks a little esoteric at first, but it's easy to use in practice, and you'll really appreciate the extra security it provides.
37
Specialization
of
CreditCardPaymentProcessor
is inherited by
StagingCreditCardPay-
Furthermore, the Web Bean manager will validate that: all API types of the superclass are API types of the Web Bean annotated @Specializes (all local interfaces of the superclass enterprise bean are also local interfaces of the subclass), the deployment type of the Web Bean annotated the superclass, and
@Specializes
there is no other enabled Web Bean that also specializes the superclass.
If any of these conditions are violated, the Web Bean manager throws an exception at initialization time. Therefore, we can be certain that the superclass with never be called in any deployment of the system where the Web Bean annotated @Specializes is deployed and enabled.
38
In either of these cases, Web Beans gives us two options: write a producer method, or declare the Web Bean using XML.
Many frameworks use XML to provide metadata relating to Java classes. However, Web Beans uses a very different approach to specifying the names of Java classes, fields or methods to most other frameworks. Instead of writing class and member names as the string values of XML elements and attributes, Web Beans lets you use the class or member name as the name of the XML element. The advantage of this approach is that you can write an XML schema that prevents spelling errors in your XML document. It's even possible for a tool to generate the XML schema automatically from the compiled Java code. Or, an integrated development environment could perform the same validation without the need for the explicit intermediate generation step.
And this is all the code we need to declare that Date is a simple Web Bean! An instance of Date may now be injected by any other Web Bean:
@Current Date date
We use exactly the same approach to specify names and binding type:
<util:Date> <Named>currentTime</Named> </util:Date>
39
<util:Date> <SessionScoped/> <myapp:Login/> <Named>loginTime</Named> </util:Date> <util:Date> <ApplicationScoped/> <myapp:SystemStart/> <Named>systemStartTime</Named> </util:Date>
Interceptors and decorators are just simple Web Beans, so they may be declared just like any other simple Web Bean:
<myfwk:TransactionInterceptor> <Interceptor/> <myfwk:Transactional/> </myfwk:TransactionInterceptor>
The <Name> element declares a simple Web Bean of scope @Dependent and class Name, with a set of initial field values. This Web Bean has a special, container-generated binding and is therefore injectable only to the specific injection point at which it is declared. This simple but powerful feature allows the Web Beans XML format to be used to specify whole graphs of Java objects. It's not quite a full databinding solution, but it's close!
40
Writing an XML schema is quite tedious. Therefore, the Web Beans RI project will provide a tool which automatically generates the XML schema from compiled Java code.
41
The Java EE @PostConstruct and @PreDestroy callbacks are also supported for all simple and enterprise Web Beans. The @PostConstruct method is called after all injection has been performed. There is one restriction to be aware of here: Beans.
@PersistenceContext(type=EXTENDED)
The Web Beans client proxy takes care of routing method invocations from the Servlet to the correct instances of Credentials and Login for the current request and HTTP session.
43
Java EE integration
obtained by direct JNDI lookup, or injection using @EJB, for example. In particular, you can use Web Beans injection in Message-Driven Beans, which are not considered Web Beans because you can't inject them. You can even use Web Beans interceptor bindings for Message-Driven Beans.
@Transactional @MessageDriven public class ProcessOrder implements MessageListener { @Current Inventory inventory; @PersistenceContext EntityManager em; public void onMessage(Message message) { ... } }
Thus, receiving messages is super-easy in a Web Beans environment. But beware that there is no session or conversation context available when a message is delivered to a Message-Driven Bean. Only @RequestScoped and @ApplicationScoped Web Beans are available. It's also easy to send messages using Web Beans.
Now we can just inject the Queue, QueueConnection, QueueSession or QueueSender for a queue, or the Topic, TopicConnection, TopicSession or TopicPublisher for a topic.
@OrderProcessor QueueSender orderSender; @OrderProcessor QueueSession orderSession; public void sendMessage() { MapMessage msg = orderSession.createMapMessage(); ... orderSender.send(msg); } @StockPrices TopicPublisher pricePublisher; @StockPrices TopicSession priceSession; public void sendMessage(String price) { pricePublisher.send( priceSession.createTextMessage(price) ); }
The lifecycle of the injected JMS objects are completely controlled by the Web Bean manager.
44
The nerve center for extending Web Beans is the Manager object.
45
public abstract boolean isSerializable(); public abstract boolean isNullable(); public abstract T create(); public abstract void destroy(T instance); }
It's possible to extend the Bean class and register instances by calling Manager.addBean() to provide support for new kinds of Web Beans, beyond those defined by the Web Beans specification (simple and enterprise Web Beans, producer methods and JMS endpoints). For example, we could use the Bean class to allow objects managed by another framework to be injected into Web Beans. There are two subclasses of Bean defined by the Web Beans specification: Interceptor and Decorator.
For example, we might implement Context to add a business process scope to Web Beans, or to add support for the conversation scope to an application that uses Wicket.
46
47