2012 04-09-v2-tdp-1167-cdi-bestpractices-final
description
Transcript of 2012 04-09-v2-tdp-1167-cdi-bestpractices-final
1
Patterns and Best Practices for Contexts for Dependency InjectionRohit Kelapure
WebSphere Application Server Caching Architect
Apache OpenWebBeans Committer
IBM
http://wasdynacache.blogspot.com/
Session ID = TDP-1167
2 Session ID: TDP 1167
Contributions from Mark Struberg, Gerhard Petracek, Arne Limburg, Reza Rahman & Ryan Cuprak
http://www.slideshare.net/os890/
https://cwiki.apache.org/confluence/display/EXTCDI/External
http://people.apache.org/~struberg
http://www.openknowledge.de/?id=62#139
http://www.slideshare.net/os890/make-jsf-more-typesafe-with-cdi-and-myfaces-codi
http://www.slideshare.net/os890/myfaces-codi-conversations
http://people.apache.org/~struberg/jax2012/cdi-basics_jax2012.pdf
http://people.apache.org/~struberg/jax2011/JSR-299_JAX_2011.pdf
http://www.rahmannet.net/downloads/cdi_best_practices.pdf
3 Session ID: TDP 1167
Agenda
Tour of CDI (Contextual Dependency Injection)
Common Pitfalls
New Programming Patterns
Best Practices
CDI Extension Programming
CDI Extensions
4 Session ID: TDP 1167
Contextual Dependency Injection JSR299
Future of JEE Loose Coupling
– Server & Client, Components, Concerns & Events
Design pattern Specific type of IoC
– Don’t call us, we will call you
– No hard coded dependencies
Inspirations SEAM, Spring, Guice Dramatic reduction in LOC Goes far beyond what was possible with Java EE5 Not only an API but also a SPI Spec compliant way to modify the EE platform via CDI
extensions
4
5 Session ID: TDP 1167
CDI Services
Contextual State and lifecycle mgmt.
Typesafe dependency injection
Interceptors and decorators
– extend behavior with typesafe interceptor bindings
SPI enables portable extensions
– integrates cleanly with Java EE
Adds the Web conversation context
– + to standard contexts (request, session, application)
Unified component model
– Integration with the Unified EL
• Enables use of EJB 3.0 components as JSF managed beans
Events decouple producers and consumers
5
6 Session ID: TDP 1167
Relationship to other Java EE Specs
Contextual lifecycle mgmt. for EJBs
– Session bean instances obtained via DI are contextual
• Bound to a lifecycle context
• Available to others that execute in that context
• Container creates instance when needed
• Container Destroys instance when context ends
Contextual lifecycle mgmt. for Managed Beans
Associate Interceptors with beans using typesafe interceptor
bindings
Enhances JSF with a sophisticated context & DI model
– Allows any bean to be assigned an unique EL name
6
7 Session ID: TDP 1167
What is a CDI Managed Bean Concrete POJO (exception for @Decorator)
– Must have a no-argument constructor or constructor annotated @Inject
– Cannot be a static inner class
– Opt in with a beans.xml
Objects returned by producers
Additional types defined by CDI SPI
Defined to be a managed bean by its EE specification– EJB session beans (local or remote)
– Message Driven Beans
– JEE Resources (DataSources, JMS Destinations)
– Persistent Unit, Persistent Contexts
– Web Service References
– Servlets, Filters, JSF Managed Beans, Tag Libraries …
Built-in Beans– JTA User Transaction
– Security Principal representing caller identity
– Bean Validator & Validation Factory
7
8 Session ID: TDP 1167
CDI Packaging
Bean classes packaged in a Bean Deployment Archive To activate CDI create a beans.xml file (can be empty)
– META-INF
– WEB-INF/classes
Container searches for beans in all bean archives in application classpath
http://java.sun.com/xml/ns/javaee/beans_1_0.xsd
8* Picture source Norman Richards
9 Session ID: TDP 1167
Types of CDI Injection
Field Injection Parameter Injection
– Observer, producer & disposer methods
– Initializer methods
@ConversationScoped
public class Order {
@Inject @Selected Product product; //Field injection
@Inject //Initializer method
void setProduct(@Selected Product product) {
this.product = product;
}
}
9
10 Session ID: TDP 1167
Bean Definition
Bean Type
Qualifier
Scope
EL Name
Interceptors
Implementation
10
11 Session ID: TDP 1167
public class BookShop extends Business implements Shop<Book>{
...}
Client visible Bean types – BookShop, Business, Shop<Book>, Object
@Stateful public class BookShopBean extends Business
implements BookShop, Auditable {...}
Bean types– BookShop, Auditable , Object
Can be restricted using the @Typed annotation – @Typed(Shop.class) public class BookShop
Bean Types
11
12 Session ID: TDP 1167
Qualifiers Distinguish between beans of the same Type @ASynchronousclass AsynchronousPaymentProcessorimplements PaymentProcessor {...}
@Synchronousclass SynchronousPaymentProcessor
implements PaymentProcessor {...}
//Qualifier type @Qualifier @Target({TYPE, METHOD, PARAMETER, FIELD})@Retention(RUNTIME)public @interface Synchronous{}
Specifying qualifiers on an injected bean aka Client@Inject @Synchronous PaymentProcessor
12
13 Session ID: TDP 1167
Qualifiers Cont…
Bean can define multiple qualifier types– Injection Point only needs enough qualifiers to uniquely identify a bean
Every bean – Built-in qualifier @Any– Default qualifier @Default when one is not explicitly declared
Producer methods and fields can also use qualifiers@Produces @Asynchronous public PaymentProcessor createAsynchronousProcessor() {
return new AsynchronousPaymentProcessor(); }
Qualifiers with members@Target({FIELD, PARAMETER}) @Retention(RUNTIME) @Qualifier public @interface Currency {
public String code(); } // client @Inject @Currency(code=“USD”) PaymentProcessor processor;
13
14 Session ID: TDP 1167
Scope: Lifecycle & Visibility of Instances
Scoped Objects exist a lifecycle context– Each bean aka contextual instance is a singleton in that context– Contextual instance of the bean shared by all objects that execute
in the same context
// Session scoped bean shared by all requests // that execute in the context of that sessionpublic @SessionScoped class ShoppingCart implements Serializable
{ ... }
@Produces @RequestScoped @Named("orders")List<Order> getOrderSearchResults() { ... }
@ConversationScoped public class Order { ... }
@Produces @SessionScoped User getCurrentUser() { ... }
14
15 Session ID: TDP 1167
Scope: Lifecycle & Visibility of Instances
Normal Scopes – @RequestScoped DTO/Models, JSF Backing beans
– @ConversationScoped Multi-step workflow, Shopping Cart
– @SessionScoped User login credentials
– @ApplicationScoped Data shared by entire app, Cache
Pseudo scope
– @Dependent (default scope) makes sense for majority
• Bound to the lifecycle of the object they were injected Qualifier
– @New (new instance will be created)
• Not bound to the declared scope
• Has had DI performed Custom scopes provided by Extensions
– OpenWebBeans provides @ViewScoped through the Jsf2ScopesExtension
15
16 Session ID: TDP 1167
EL Name: Lookup beans in Unified EL
Binding components to JSF Views
Specified using the @Named annotationpublic @SessionScoped @Named("cart“) class ShoppingCart implements
Serializable { ...
}
Now we can easily use the bean in any JSF or JSP page<h:dataTable value="#{cart.lineItems}" var="item">...</h:dataTable>
Container derives default name in absence of @Named– Unqualified short class name of the bean class
@Named is a built-in Qualifier
16
17 Session ID: TDP 1167
Interceptors
Separate cross-cutting concerns from business logicAssociate Interceptors to beans using Interceptor Bindings@Inherited @InterceptorBinding //Interceptor Binding Type definition@Target({TYPE, METHOD})@Retention(RUNTIME)public @interface Transactional {}
//Declaring Interceptor Bindings of an Interceptor@Transactional @javax.interceptor.Interceptorpublic class TransactionInterceptor {@AroundInvokepublic Object manageTransaction(InvocationContext ctx)
throws Exception { ... }}
//Binding Interceptor to Bean@Transactional public class ShoppingCart { ... }public class ShoppingCart {
@Transactionalpublic void placeOrder() { ... }
}
17
18 Session ID: TDP 1167
Interceptors
Enabled manually in the beans.xml
Order defined in beans.xml
@Dependent object of the object it intercepts
<beans>
<interceptors>
<class>org.mycompany.TransactionInterceptor</class>
<class>org.mycompany.LoggingInterceptor</class>
</interceptors>
</beans>
18
19 Session ID: TDP 1167
Implementation Design Patterns
Bean Implementation provided by
– Developer … Java class
– Container … Java EE env. Resource beans
Contextual Singleton
All Normal scoped beans are proxied
– Contextual Reference implies a proxy for a contextual instance
Dynamic Proxies
– Passivation of contextual instances
– Scope Management
• Narrower scope injected into a wider scope
Chaining of Dynamic Proxies
– Interceptor and Decorator chaining
19
20 Session ID: TDP 1167
Alternatives
Deploy time selection of bean implementation
Alternate implementation of bean
– Explicitly enabled in the beans.xml
– Overrides the original bean@Alternative // annotate bean class, producer method or field
@Specializes
public class MockOrder extends Order {
... // alternative implementation
}
<beans>
<alternatives>
<class>org.example.MockOrder</class>
</alternatives>
</beans>
@Specializes – Alternate inherits the metadata (name, qualifiers ) etc of the parent
20
21 Session ID: TDP 1167
Stereotypes
Stereotype bundles
– Scope
– Interceptor Bindings
– @Named
– @Alternative
Bean annotated with a stereotype inherits all annotations of the stereotype
@RequestScoped
@Secure
@Transactional
@Named
@Stereotype
@Target(TYPE)
@Retention(RUNTIME)
public @interface Action {}
Built-in Stereotypes
– @Model
– @Decorator
– @Interceptor
@Alternative applied to a stereotype
– ALL beans with that stereotype are enabled/disabled as a group
21
22 Session ID: TDP 1167
Producers
Application control of bean instance creation & destruction Producer Fields
public class Shop {@Produces @ApplicationScoped @Catalog @Named("catalog")List<Product> products = ....;
}
Producer Methodspublic class Shop {
@Produces @ApplicationScoped @Catalog @Named("catalog")List<Product> getProducts(CatalogID cID) { ... }
}
Disposer Methods– Customized cleanup of object returned by a producer methodpublic class Shop {
public void close(@Disposes @Catalog List<Product> products) {products.clear();
}}
22
23 Session ID: TDP 1167
Injecting Java EE Resources
When injecting EJBs
– Use @Inject to get Contextual Injection
– Use @EJB ONLY for remote session beans
Define producers making EE types available for injection… non contextual injection@Produces @WebServiceRef(lookup="java:app/service/PaymentService")
PaymentService paymentService;
@Produces @PersistenceContext(unitName="CustomerDatabase")
@CustomerDatabase EntityManager customerDatabasePersistenceContext;
Consume the Injected types in other CDI Beans@Inject @CustomerDatabase EntityManager myEntityManager
@Inject PaymentService myPaymentService
23
24 Session ID: TDP 1167
Decorators Implements one or more bean types
– Can be abstract– Implements the interface it is decorating
Extend bean types with function specific to that type Called after interceptors Explicitly enabled in the beans.xml
public interface Htmlable { String toHtml(); } //interface
public class HtmlDate extends Date implements Htmlable { public String toHtml() { //date class that knows its HTML representation
return toString(); }
}
@Decorator public class StrongDecorator implements Htmlable { @Inject @Delegate @Any private Htmlable html; public String toHtml() { //decorator that puts the HTML inside <strong> tags
return "<strong>" + html.toHtml() + "</strong>"; }
}24
25 Session ID: TDP 1167
Events: Observer Pattern public class MyEvent { String data; Date eventTime; .... } // Event is a POJO
// Event<MyEvent> is injected automatically by the container @Stateless @Named (“producer”)public class EventProducer { @Inject @My Event<MyEvent> event; //@My is a Qualifier public void doSomething() {
event.fire(new MyEvent()); } }
// Declare method that takes a parameter with @Observes annotation@Stateless // Transactional, Conditional observerpublic class EventConsumer {
public void afterMyEvent(@Observes(during=AFTER_SUCCESS receive=IF_EXISTS) @My MyEvent event) {
// .. Do something with MyEvent}
}
<h:form> // Fire the event from a JSF 2.0 Page <h:commandButton value="Fire!" action="#{producer.doSomething}"/>
</h:form>25
26 Session ID: TDP 1167
CDI Implementation in WAS
WAS CDI implementation is based on Apache OpenWebBeans
– OpenWebBeans release 1.0.0 + fixes
Changes & additions on top of OpenWebBeans to support integration with the server run time
– Integration with other Java EE containers like JSF, Servlet & EJB container that support injectable components.
– Proprietary ScannerService implementation
– Direct use of EJB metadata for determining EJB types.
– Automatic registration of Servlet Listeners, Filters, Interceptors for CDI applications
– WebSphere Application Server-specific implementations of many OpenWebBeans Service Programming Interfaces (SPI)
• ResourceInjectionService, TransactionService, failover service…
26
27 Session ID: TDP 1167
CDI Bean Failover in WAS
WAS supports failover (activation & passivation) of CDI beans & enterprise beans, along with their interceptors and decorators
EJB failover support with CDI ONLY works for Stateful Session Beans and requires the same configuration as Stateful Session Bean failover
Configure EJB failover with web HTTP session failover
Except for abstract decorators, failover services are based on current WAS failover providers
Both Web session failover and EJB SFSB failover need to configured
27
28 Session ID: TDP 1167
PitfallsCDI Exception How to Fix
AmbiguousResolutionExceptionMore than one bean eligible for injection.
Add qualifiers or alternatives to @Inject to narrow bean resolution. Disable alternatives. Annotate class with @Typed
UnproxyableResolutionExceptionbean type cannot be proxied by the container
Ensure that bean types are legal. Check if class is declared final, has final methods or private CTOR with no parameters
UnsatisfiedResolutionExceptionNo bean eligible for injection
Remove qualifiers from injection point or add qualifiers to existing types. Enable alternatives
BusyConversationExceptionRejected request b/c concurrent request is associated with the same conversation context.
Before starting a conversation check if one already exists using Conversation.isTransient()
NonexistentConversationExceptionConversation context could not be restored
Check if conversation is being propagated with the cid GET request parameter
ContextNotActiveExceptionBean invocation in inactive context
Ensure that methods on the injected bean are called in a context that is active as defined by the scope of the bean.
ObserverExceptionException is thrown while handling event
Fix observer method code that throws the exception.
29 Session ID: TDP 1167
Program pattern - @Typed
Defines the Java Type which should be used for that bean
Possible to 'disable' a bean with @Typed() to prevent AmbiguousResolutionExceptions@Typed() // basically disables this Bean public class MenuItem implements Serializable {
@ApplicationScopedpublic class TrackReadOnlyService {
public Track readTrack(int id) {..}}@Typed(TrackReadWriteService.class)@ApplicationScopedpublic class TrackReadWriteService extends TrackReadOnlyService
{public void writeTrack(Track t) {..}
}
Handy for subclasses which should NOT conflict with their parent class types
30 Session ID: TDP 1167
Program pattern - Cache Cleaning via Events
If user changes the language or logs in, then we need to change the menu@SessionScopedpublic class Menu {
private @Inject MenutItem parentMenu;protected reloadMenu(@Observes UserSettingsChangedEvent uscE) {
parentMenu = menuSvc.loadMenu();}
}
31 Session ID: TDP 1167
Program Pattern – Type safe configuration
Configuration without properties or XML files
Just create an Interface or default config class
– public interface MyConfig{
– String getJdbcUrl();
– }
Later just implement @Specializes, @Alternative, @ProjectStageActivated, @ExpressionActivated for your configuration implementation
32 Session ID: TDP 1167
Program Pattern - Dynamic Resolution
@Inject @Any private Instance<BusService> busSvcSource;private BusService busSvc;..@PostConstructprotected init() {
busSvc = busSvcSource.select(user.isAdmin()
? new AnnotationLiteral<Default>() {}
: new AnnotationLiteral<Cached>() {}
).get();}
@Inject @Any Instance<ServiceX> svcXSource;..@Produces @TenantSpecific @RequestScopedpublic ServiceX getSvcX(Tenant t) {
return svcXSource.select(..).get();}
33 Session ID: TDP 1167
Best Practice- Scopes
Scopes – Components with
appropriate scopes maximize
memory usage.
Scopes remove boilerplate
state maintenance code –
especially at presentation tier.
Make judicious use of
conversations
Leverage appropriate
conversation scope from
MyFaces CODI
34 Session ID: TDP 1167
Best Practice - Qualifiers with enums
@Qualifier
@Retention(RUNTIME)
@Target({ FIELD, TYPE })
public @interface Admin {}
@Qualifier
@Retention(RUNTIME)
@Target({ FIELD, TYPE })
public @interface Support {}
@Inject @Support
private MailSender
mailSender;
@Qualifier
@Retention(RUNTIME)
@Target({ FIELD, TYPE })
public @interface
MailSenderType {
MailSenderTypes value();
}
public enum MailSenderTypes {
ADMIN, SUPPORT
}
@Inject
@MailSenderType(MailSenderTypes
.SUPPORT) private MailSender
mailSender;
35 Session ID: TDP 1167
Best Practice – Interceptors, Decorators, Naming
Interceptors are designed for system-level crosscutting concerns very decoupled from business logic.
Decorators intended for concerns that should be compartmentalized but are still very close to business logic.
Naming Only necessary to name components that are referenced outside of Java such as in JSF/Facelets via EL.
Naming Default names are good enough.
Naming Be careful of naming conflicts – name-based resolution not type-safe!
Naming Choose names that add to readability/API fluency
Stereotypes Encapsulate specialized behavior & component groups of recurring metadata
36 Session ID: TDP 1167
Best Practice - Injection
Declarative, static injection sufficient for most cases
@Produces & @Disposes used when objects must be programmatically created/destroyed
Look-up using Instance used when dependencies need to be resolved at runtime
For dynamic resolution BeanManager.getBeans SPI can be used
Java EE 5 introduced limited, specialized, name-based resource injection via annotations - @Resource, @EJB, @PersistenceContext
CDI provides powerful, general purpose, type-safe injection
Favor CDI for greater flexibility
37 Session ID: TDP 1167
Best Practice - Integration CDI & EJB
Use EJB for enterprise services – like transactions, security
Use CDI managed beans otherwise – EJB does have overhead
Avoid plain managed beans
EJBs most appropriate for service/business logic tier
CDI makes it possible to use EJBs as JSF managed beans – only good practice for RAD/prototyping
Using EJB services without EJB
Interfaces are optional
38 Session ID: TDP 1167
Best Practice- How to deal with Interfaces
Interfaces are evil
In Java EE 6 interfaces are absolutely optional
– Inject classes directly
Eliminate interfaces
– Considerably reduce the number of files
– Code becomes easier to understand & maintain
Not needed for decoupling purposes
– EJB 3.1 / CDI bean be directly exposed over SOAP or REST
Should be used ONLY for
– Strategy Pattern
– Layering
– APIs (not very common)
39 Session ID: TDP 1167
Best Practice - Integration CDI & JSF
CDI more powerful replacement to JSF managed beans
Seam Faces and CODI MyFaces excellent enhancements
CDI naming, scopes, producers, qualifiers work in powerful ways with JSF
CDI designed with JSF in mind, but not limited to JSF
Wicket, Struts 2, Spring MVC have CDI extensions for integration.
Replace all JSF managed beans and scopes with corresponding CDI scopes
40 Session ID: TDP 1167
Best Practice - Integration CDI & JPA
CDI/JPA bean life-cycle mismatch
Life-cycle mismatch bridged by CDI producers
Make judicious use of stateful session bean extended persistence context
Use the entitymanager-per-request design pattern by producing your EntityManager@RequestScopedpublic class EntityManagerProducer {
private @PersistenceContext(unitName="Course") EntityManager entityManager;
@Produces @RequestScoped @QCourse public EntityManager createEntityManager() {
return entityManager; } public void dispose(@Disposes @QCourse EntityManager entityManager) {
entityManager.close(); }
}public class MyUserService {
private @Inject @QCourse EntityManager em;public User getUser(long id) {
return em.find(id, User.class);}
41 Session ID: TDP 1167
Best Practice - Integration CDI & JMS
Message Producer & JMS Abstraction
42 Session ID: TDP 1167
Best Practice - CDI as replacement for JMS
CDI provides simple lightweight local transactional publish-subscribe communication
Main goal of JMS 2.0 will be simplicity & tight integration with CDI
JMS pub-sub implementation is more powerful, flexible; however requires more code
CDI events are delivered synchronously
Events can be asynchronously consumed with an EJB 3.1 bean.
@Stateless public class MessageListener {
@Asynchronous public void onImportantMessage(
@Observes @Importance(Importance.Degree.HIGH)String message){ System.out.println("+Important " + message);
} }
43 Session ID: TDP 1167
CDI Extensions
Portable
– Activated by dropping jars on the application classpath
– Loaded by the java.util.ServiceLoader
– Tools/utilities, extending Java EE, integration with Java EE APIs & integrating with
non-standard APIs, environments
Service provider of the service javax.enterprise.inject.spi.Extension declared in
META-INF/services
Code to javax.enterprise.inject.spi .* interfaces
Integrate with container through container lifecycle events by
– Providing its own beans, interceptors and decorators
– Injecting dependencies into its own objects
– Providing a context implementation for a custom scope
– Augmenting or overriding the annotation-based metadata with other source
43
44 Session ID: TDP 1167
CDI Extension code sample
My Faces CODI ProjectStageActivated
Before Bean
Discovery
• Add scope• Add annotated
type• Add
interceptor binding
• Add stereotype
Process Annotated
Type
• Veto bean candidate
• Replace annotated type
Process Injection Targets
• Replace Injection target
Process Beans
• Prepare additional beans
After Bean Discovery
• Add bean• Add context• Add observer
method
After Deployment Validation
• Assessment
45 Session ID: TDP 1167
Apache MyFaces CODI
JSF 1.2 and 2.x
Type-safety
Extensibility
Advanced Scopes
Various Events
View-Controller
JSF 2 Scope Mappings
Type-safe View-Configs
Type-safe Navigation
JPA Integration
Dependency Injection Support for Bean Validation
Advanced I18n
Scripting Integration
And more!
Modules
– –JSF Module (for 1.2 and 2.0 and 2.1)
– –JPA Module
– –BV Module
– –I18n-Message Module
– –Scripting Module
– –Test Support Modules
46 Session ID: TDP 1167
Seam 3
Drools
Faces (JSF)
International
Security
XML Bean Config
Seam 2 Backwards Compatiblity
Spring Intergration
jBPM
JAX-RS
Servlet
Transactions and Persistence
Document Management
Seam Application Framework
JMS
JBoss ESB
Errai/GWT
Remoting
Scheduling
Environment Configuration
47 Session ID: TDP 1167
Future of CDI Extensions – Apache DeltaSpike
Users have to look at several CDI extensions like JBoss, Seam 3 and Apache MyFaces CODI as well as smaller projects & blog entries
No out of- the-box optimization across different extension-projects
To combine them sometimes requires a detailed knowledge about the projects.
Goal: Add and/or merge most features in/into DeltaSpike
First releases - the community started to merge features of Apache My-Faces CODI-Core and JBoss Solder which is a central part of Seam3
48 Session ID: TDP 1167
CDI 1.1 & Future
Globally enabled Interceptors and Alternatives
XML configuration
@Disposes for producer fields
Clarifies some AnnotatedType behavior
@Veto + optional beans
Relax Serialization rules
@EarApplicationScoped vs @WebApplicationScoped
https://issues.jboss.org/browse/CDI
Closer integration between other EE specs like EJB and CDI beans
Transactions, Security, Concurrency etc delivered as Interceptors that can be applied to any CDI bean
Popular CDI portable extensions rolled into DeltaSpike and future specifications
48