Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Extending Java EE with CDI and JBoss Forge

2,472 views

Published on

Slides of the university I gave at Devoxx Belgium with Antonio Goncalves on CDI, Java EE and JBoss Forge.
Abstract:
-------
During this 3 hours university, you will learn some CDI basis, and will quickly dive into more advance CDI features (such as extension). Using JBoss Forge we will quickly generate a Java EE 7 web application, and then, following business requirements, we will add CDI functionalities.
This university talk will be a mixture of code and slides, focusing on CDI and Java EE 7.
--------
Video of the university is available on YouTube: http://youtu.be/LYKMaj4XKvg
Code and Slides on GitHub: https://github.com/antoinesd/cdi-forge-uni/tree/DevoxxBe2015

Published in: Software

Extending Java EE with CDI and JBoss Forge

  1. 1. Extend Java EE with CDI Antoine Sabot-Durand · Antonio Goncalves
  2. 2. Antoine Sabot-Durand  Red Hat  CDI spec lead  @antoine_sd  next-presso.com  github.com/antoinesd
  3. 3. Antonio Goncalves  Java Champion  Contractor developper  antoniogoncalves.org  @agoncal  github.com/agoncal
  4. 4. Agenda  Meet ExpensEEs  Meet CDI  Basic DI  Producers  Interceptors  Qualifiers  Programmatic Lookup  Contexts  Events  Extensions  Conclusion
  5. 5. Expenses made EEsy Meet ExpensEEs
  6. 6. We were asked to build this app  A web app to manage expenses  Based on Java EE 7  CDI 1.2  JPA 2.1  JSF 2.2  JAX-RS 2.0  Able to integrate legacy libs
  7. 7. Storyboard - Home Screen
  8. 8. Storyboard - Login Screen
  9. 9. Storyboard - New User Screen
  10. 10. Storyboard - Logged In
  11. 11. Storyboard - Profile
  12. 12. Storyboard - Expense Detail
  13. 13. Storyboard - Expense Confirm
  14. 14. Storyboard - View Expenses
  15. 15. Architecture - Model User String login String password String name String email UserRole role Reimbursement Date date Set<Expense> expenses Currency currency User user Conference conference Expense description Date date Float amount ExpenseType expenseType private Currency currency Conference String name Date date String country String city 1 * 1 * 1 *
  16. 16. Let’s Forge ExpensEEs!
  17. 17. JBoss Forge Forge 1. Generation & Scaffolding tool 2. Shell commands & IDE Integration 3. Gets you start quickly 4. Takes care of integration 5. Plugin based 6. Java EE…​ But anything else really 7. More info on forge.jboss.org/
  18. 18. Back to demo  Run JBoss Forge  Create JPA Entities  Scaffold JSF CRUD pages  Scaffold REST Endpoint  Run Forge script
  19. 19. Architecture Forge html web rest model ConferenceCRUDExpenseCRUD ReimbursementCRUD UserCRUD ConferenceBeanExpenseBean ReimbursementBean UserBean ConferenceEndpoint ConferenceExpenseService Reimbursement User useuse use use useuseuse use use
  20. 20. Not Finished Yet 1. Only a CRUD app 2. No "Create expenses" Wizard 3. No Login/logout 4. Primefaces 5. Java EE 6 instead of Java EE 7 6. JTA 1.2 @Transactional 7. JPA 2.1 Insert data in DB 8. Not in full CDI programming model !
  21. 21. Back to demo  Refactor the app  Add a service layer  Add Java EE 7 dependencies  Less EJB More CDI  Webjar for Twitter Bootstrap  2 hours later…​
  22. 22. New Architecture html web rest service model ConferenceCRUDExpenseCRUD ReimbursementCRUD UserCRUD ConferenceBeanExpenseBean ReimbursementBean UserBean ConferenceEndpoint ConferenceServiceExpenseService ReimbursementService UserService ConferenceExpense Reimbursement User useuse use use @Inject@Inject@Inject @Inject @Inject useuse use use
  23. 23. Contexts and Dependency Injection Meet CDI
  24. 24. What is CDI?  A Java EE spec.  ASL2 (even TCK)  Launched in Java EE 6  2 releases since launch  1.2 is last (Java EE 7)  2.0 on its way  2 major impl.  JBoss Weld (RI)
  25. 25. What’s included? CDI Provides: 1. A powerful programming model 2. Different Lifecycles for stateful objects 3. A Typesafe DI mechanism 4. The ability to decorate injected objects 5. The ability to intercept methods 6. An event notification model 7. A SPI allowing portable extensions to integrate 3rd party tech
  26. 26. “ CDI explained to your boss CDI brings a powerful and modern programming model that standardizes good practices. CDI provides a consistent and seamless way to integrate 3rd party frameworks to this model. Once adopted, CDI will become the invisible backbone of our projects.
  27. 27. “ One container to rule them all  The container is the heart of CDI  The container checks all possible CDI code at boot time  The container manages your components lifecycle and services  Yet, you’ll never see it (except in advanced development) Container is your application’s invisible conductor
  28. 28. @Inject
  29. 29. Almost Everything is a bean  During boot time, the container scans each class to check if it meets the conditions to be a bean  These conditions are very loose so, most pojo classes are beans public class HelloService { public String hello() { return "Hello World!"; } } HelloService is a concrete class with a default constructor, therefore it’s a bean 1 1
  30. 30. Injection in bean field  A bean can inject other beans or be injected into other beans public class MyBean { @Inject private HelloService service; public void displayHello() { display(service.hello(); } } @Inject annotation defines an injection point (here a field) The container looksa for the bean to inject by its type (here HelloService ) 1 2 1 2
  31. 31. Injection in bean constructor  To become a bean a class must have a constructor with no parameters or a constructor annotated @Inject public class MyBean { private HelloService service; @Inject private MyBean(HelloService service) { this.service = service; } }  Only one constructor can be annotated with @Inject
  32. 32. Injection in a method  Such method is called initializer methods public class MyBean { private HelloService service; @Inject public void initService(HelloService service) { this.service = service; } }  all initializer methods are called at bean instantiation
  33. 33. There could be only one  Each injection point is checked at boot time:  If no bean is eligible for it, the injection point is unsatisfied  If multiple beans are eligible for it, the injection point is ambiguous  In both case a DeploymentException is thrown by the container
  34. 34. Back to demo  Inject a Logger into UserService  Log a message in persist method
  35. 35. When you don’t own your bean’s class, use Producers
  36. 36. What is a producer?  A way to declare a bean…​  …​from a field or a method…​  …​for a class you don’t own…​  …​or a non CDI class.
  37. 37. Declaring a Producer public class ProducingBean { @Produces private List<Integer> mapInt = new ArrayList<>(); @Produces @French public List<String> FrenchListStrProducer() { List<String> res = Arrays.asList("bonjour"); return res; } } Producers should be declared in a Bean class It’s a field or a method annotated with @Produces (static methods supported) Producers can have qualifiers value of field or method returned value is the bean instance 1 2 2 3 4 1 2 3 4
  38. 38. Producers are an other kind of bean  they can have qualifiers like any other bean  producer method (or field) is called to create the bean instance public class MyBean { @Inject @French private List<String> frenchListStrBean; @Inject public MyBean(List<Integer> initValues) { ... } }
  39. 39. Disposers are Producer’s housekeeping  Disposers can free resources when produced bean is destroyed public class ProducingBean { @Produces public MyNonCDIClass myProducer() { return new MyNonCdiClass(); } public void releaseMyInstance(@Disposes MyNonCdiClass inst) { inst.clean(); } } Will be called at the bean instance end of life 1 1
  40. 40. Producer methods can have parameters  Parameters will be injected by the container at instantiation time  They should have matching beans otherwise deployment fails public class ProducingBean { @Produces public MyNonCDIClass listStrProducer(MyBean bean) { ... } } The container resolves MyBean and passes it to the producer when an instance of the bean is requested 1 1
  41. 41. Back to demo  The class LoggerProducer produces a Logger  Injecting a logger in service doesn’t change
  42. 42. Accessing Injection Point metadata  The container can provide metadata about the Injection Point public class ProducingBean { @Produces public Logger produceLog(InjectionPoint injectionPoint) { return Logger.getLogger(injectionPoint.getMember() .getDeclaringClass().getName()); } } InjectionPoint is a SPI interface. The container can inject it to give info about the injection point 1 1
  43. 43. Back to demo  Add InjectionPoint API to the producer  Injecting a logger in service doesn’t change
  44. 44. Interceptors and Decorators
  45. 45. Interceptors vs Decorators  They are both Aspect Oriented Programming tools  Interceptors are technical oriented: transaction, security, logging  Interceptors are bound to any bean or bean method  Decorators are business oriented: change the behaviour of a bean  Decorators are bound to a bean type
  46. 46. Interceptors  Interceptors are defined in their own specification  User should create an annotation as an interceptor binding  Interceptor class must be bound to an interceptor binding  Interceptor class contains intercepting methods which are bound to lifecycle phase of intercepted bean  Interceptor binding is added to intercepted bean or method
  47. 47. Interceptor binding example  An interceptor binding is annotation used in two different places  On the interceptor and on the intercepted element to link them @InterceptorBinding @Target({METHOD, TYPE}) @Retention(RUNTIME) public @interface Loggable {} This annotation comes from interceptor specification. It makes @Loggable an interceptor binding 1 1
  48. 48. Interceptor example @Interceptor @Loggable @Priority(Interceptor.Priority.APPLICATION) public class LogInterceptor { @AroundInvoke public Object log(InvocationContext ic) throws Exception { System.out.println("Entering " + ic.getMethod().getName()); try { return ic.proceed(); } finally { System.out.println("Exiting " + ic.getMethod().getName()); } } } We make the class an interceptor ( @Interceptor comes from Interceptor spec) We bind this interceptor to the @Loggable interceptor binding We activate and give a priority to the interceptor (can be done in beans.xml config file as well) This intercepting method will be invoked instead of the intercepted one. 1 2 3 4 1 2 3 4
  49. 49. Interceptor Usage @Loggable public class MyBean { ... } public class MyOtherBean { @Loggable public String hello() { ... } } All bean’s method will be intercepted This method will be intercepted 1 2 1 2
  50. 50. Decorators  To be decorated, a bean should implement an interface  The decorator has to implement the same interface  It is declared with @Decorator annotation  It can be an abstract class (lets you choose methods to decorate)  It injects the decorated bean with @Delegate annotation
  51. 51. Decorator Example @Decorator @Priority(Interceptor.Priority.APPLICATION) public abstract class HelloDecorator implements HelloService { @Inject @Delegate HelloService service; public String hello() { return service.hello() + "-decorated"; } } Declares that the class is a decorator Activates and gives a priority to the decorator (could be done via beans.xml file) Decorators can be abstract and should share the same interface than decorated beans Decorated bean is annotated with @Delegate (other beans could be injected in decorator) Decorating method is called instead of decorated bean method. Delegate can be used in it. 1 2 3 4 5 1 2 3 4 5
  52. 52. Back to demo Create a @Loggable interceptor binding  Create a LoggingInterceptor  Add LoggingInterceptor to each service  Enable interceptor in the beans.xml
  53. 53. Solving ambiguous cases with Qualifiers
  54. 54. When an injection point resolves to multiple beans… public class MyBean { @Inject HelloService service; } public interface HelloService { public String hello(); } public class FrenchHelloService implements HelloService { public String hello() { return "Bonjour tout le monde!"; } } public class EnglishHelloService implements HelloService { public String hello() { return "Hello World!"; } }  deployment fails with an Ambiguous dependencies error.
  55. 55. The solution is to create Qualifiers… @Qualifier @Retention(RUNTIME) @Target({FIELD, TYPE, METHOD, PARAMETER}) public @interface French { } @Qualifier @Retention(RUNTIME) @Target({FIELD, TYPE, METHOD, PARAMETER}) public @interface English { }  Qualifiers are annotations annotated with @Qualifier
  56. 56. to qualify beans sharing the same type…​ @French public class FrenchHelloService implements HelloService { public String hello() { return "Bonjour tout le monde!"; } } @English public class EnglishHelloService implements HelloService { public String hello() { return "Hello World!"; } }
  57. 57. and distinguish them at injection points. public class MyBean { @Inject @French HelloService serviceFr; @Inject @English HelloService serviceEn; }  Qualifiers are types enhancing injection, yet keeping it strong typed
  58. 58. Qualifiers can have members @Qualifier @Retention(RUNTIME) @Target({FIELD, TYPE, METHOD, PARAMETER}) public @interface Language { LangChoice value(); @Nonbinding String description() default ""; public enum LangChoice { FRENCH, ENGLISH } } @Nonbinding member’s value isn’t used in bean resolution mechanism 1 1
  59. 59. Binding members limit the number of annotations to add to your code @Language(FRENCH) public class FrenchHelloService implements HelloService { public String hello() { return "Bonjour tout le monde!"; } } @Language(ENGLISH) public class EnglishHelloService implements HelloService { public String hello() { return "Hello World!"; } } public class MyBean { @Inject @Language(value = FRENCH, description = "ici on parle français") HelloService serviceFr; @Inject @Language(value = ENGLISH, description = "english spoken here") HelloService serviceEn; }
  60. 60. Multiple qualifiers  A bean can have multiple qualifiers @Language(FRENCH) @Console public class FrenchHelloService implements HelloService { public String hello() { return "Bonjour tout le monde!"; } } public class MyBean { @Inject @Language(FRENCH) @Console HelloService serviceFr; } @Console is a qualifier 1 1
  61. 61. Multiple qualifiers  Injection point can have a non empty subset of the bean’s qualifiers @Language(FRENCH) @Console public class FrenchHelloService implements HelloService { public String hello() { return "Bonjour tout le monde!"; } } public class MyBean { @Inject @Console HelloService serviceFr; }
  62. 62. Multiple qualifiers  Injection point can’t have a super set of bean’s qualifier @Language(FRENCH) @Console public class FrenchHelloService implements HelloService { public String hello() { return "Bonjour tout le monde!"; } } public class MyBean { @Inject @Language(FRENCH) @Console @Language(CANADIAN) HelloService serviceFr; } Unsatisfied injection point: deployment fails 1 1
  63. 63. Built-in qualifiers  @Named set bean name for weak typed environment (EL, Javascript)  @Default added to beans without qualifier or having only @Named  @Any added to all beans for programmatic lookup and decorators  @Intercepted & @Decorated to inject intercepted bean in an interceptor / Decorator  @Initialized to qualify events when a context is started
  64. 64. Examples public class MyBean { ... } @Named public class MyBean2 { ... } @Named @Language(FRENCH) public class MyBean2 { @Inject MyBean2 bean; } this bean has @Default and @Any qualifiers this bean has @Default , @Named and @Any qualifiers this bean has @Language(FRENCH) , @Named and @Any qualifiers this injection point has @Default qualifier 1 2 3 4 1 2 3 4
  65. 65. Bean Types  A bean has a set of types depending on its definition  Bean types set for a managed bean contains the bean class, every superclass and all interfaces it implements directly or indirectly.  Bean types can be restricted by using @Typed annotation ( Object is always in the set)
  66. 66. Bean types Example public interface HelloService extends Hello { ... } public class MyBean { @Produces public HelloService prodHelloService() { ... } } 

public class FrHelloService extends GenericService<String> implements HelloService { ... } 

@Typed({HelloService.class}) 
public class FrHelloService extends GenericService<String> implements HelloService { ... } class MyBean and Object HelloService , Hello and Object FrHelloService , GenericService<String> , HelloService , Hello and Object HelloService and Object 1 2 3 4 1 2 3 4
  67. 67. Parameterized types count  Parameterized types information is kept (i.e. no type erasure) public class MyBean { @Inject Service<User> userService; @Inject Service<Staff> staffService; }  Injection points above can be satisfied without ambiguity
  68. 68. Performing Typesafe Resolution  When resolving a bean to be injected to an injection point…​  …​the container considers bean type and qualifiers.  If the bean has a bean type that matches injection point type…​  …​and the bean has all the qualifiers of the injection point…​  …​the bean is assignable to the injection point.
  69. 69. Back to demo Create a @Clear and @Encrypted qualifiers  Create a DigestPassword interface  Two implementations ClearPassword and EncryptPassword  Inject encrypted password digest into AccountBean and UserService
  70. 70. Resolving beans at runtime Programmatic Lookup
  71. 71. Meet Instance interface  Instance interface lets perform typesafe resolution at runtime public class MyBean { @Inject Instance<HelloService> service; public void displayHello() { display(service.get().hello()); } } Instance<T> injection points are always satisfied and never fail at deployment time with Instance<T> you control when bean a instance is requested with the get() method 1 2 1 2
  72. 72. Check bean existence at runtime  Instance<T> contains methods to test bean resolution public class MyBean { @Inject Instance<HelloService> service; public void displayHello() { if (!(service.isUnsatisfied() || service.isAmbiguous())) { display(service.get().hello()); } } } if tou skip the test get() may throw an exception 1 1
  73. 73. Looping on all beans in the Instance  Instance<T> is iterable  It’s where we use the @Any qualifier present on all beans public class MyBean { @Inject @Any Instance<HelloService> services; public void displayHello() { for (HelloService service : services) { display(service.hello()); } } }
  74. 74. Select a bean by its qualifier  AnnotationLiteral is a helper class to create an annotation instance public class MyBean { @Inject @Any Instance<HelloService> services; public void displayHello() { display( services.select(new AnnotationLiteral()<French> {}).get()); } } select() also accepts a type.  you can use TypeLiteral to create instances of parameterized types 1 1
  75. 75. Back to demo  Loop through all DigestPassword  Thanks to @Any  Use either @Encrypted or @Clear
  76. 76. Beans life and death Contexts
  77. 77. Bean, Scope and Contexts  All Beans have a scope defined by an annotation  Each scope is associated to a context object  So each bean is bound to a context  The context is in charge of creating bean instances  The Container is in charge of creating and destroying contexts
  78. 78. Available scopes  CDI provides the following built-in scopes (and associated contexts):  @Dependent (default) bean has the same scope than its injector  @ApplicationScoped instance is linked to application lifecycle  @SessionScoped instance is linked to http session lifecycle  @RequestScoped instance is liked to http request lifecycle  @ConversationScoped lifecycle manually controlled within session  Instance is created at first request and destroyed with its bound context  CDI SPI allows third party to create their custom contexts
  79. 79. Examples public class BaseHelloService implements HelloService { ... } @RequestScoped public class RequestService { @Inject HelloService service; } @ApplicationScoped public class ApplicationService { @Inject RequestService service; } Bean has default scope @Dependent , instances are created for each injection Bean is @RequestScoped . Instance is created by request context and destroyed with request context Bean is @ApplicationScoped . Instance is created by application context and will live during all application No problem to inject bean from an other scope: CDI will provide the right bean 1 2 3 4 1 2 3 4
  80. 80. Good to know  instances are created when first accessed not with their context  A bean instance is a singleton in its context  With SPI you can manually destroy an instance in a context  A context can be inactive without being destroyed  Request context is more than a mapping to ServletRequest lifecycle
  81. 81. More on @ConversationScoped  A context whose lifecycle is controlled by the dev  Conversation is transient or long running  It’s promoted to long running with Conversation built-in bean…​  …​ by calling begin() method…​  …​ and terminated with end() .  There can be multiple conversations in one Http Session  Conversation id is used to retrieve the active conversation
  82. 82. Example @ConversationScoped public class WizardBean { @Inject Conversation conversation; public startWizard() { ... conversation.begin() ... } public endWizard() { ... conversation.end() ... } }
  83. 83. Back to demo  Wizard to create new expenses  Use of @ConversationScoped  JSF deals with cid
  84. 84. Contexts are not always convenient  Lifecycle magic management can limit you  Bean instances are destroyed only when context ends  And you might want to get rid of instance but keep the context  CDI 1.1 added a programmatic way to destroy an instance
  85. 85. Example - Logout and session invalidation @Inject FacesContext facesContext; public String logout(){ try{ facesContext.getExternalContext().invalidateSession(); return "success?faces-redirect=true"; } catch(Exception e){ return "error"; } } redirect is important to end current request and kill the session effectively  we might want to keep the current session and logout user  not a good practice to call UI layer from service to perform business 1 1
  86. 86. Example - Do logout in a CDI way @Named @SessionScoped public class AccountBean implements Serializable { @Inject private Instance<AccountBean> myInstance; public String doLogout() { myInstance.destroy(myInstance.get()); return "/index"; } Since CDI 1.1, Instance provides a destroy() method to remove an instance from its context 1 1
  87. 87. Back to demo  AccountBean injects a reference to itself  AccountBean destroys it for logout
  88. 88. Alternatives & Stereotypes
  89. 89. Alternatives  An alternative is a bean that must be explicitly selected  When selected it superseeds beans sharing the same type  A convenient way to create Mock or specific implementation  An alternative can be activated in beans.xml or with @Priority
  90. 90. Alternative Example public SapService implements ErpService { } @Alternative public MockSapService implements ErpService { } @ApplicationScoped public OrderService { @Inject ErpService service; } A service doing heavy or costly operation Can be mocked with an alternative Beans injecting the service have nothing to do. Alternative will replace the original bean if selected 1 2 3 1 2 3
  91. 91. Selecting the Alternative  With @Priority annotaion (for the whole app) @Alternative @Priority(Interceptor.Priority.APPLICATION) public MockSapService implements ErpService { }  With beans.xml file (for the current module) <beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"> <alternatives> <class>com.acme.MockSapService</class> </alternatives> </beans> 1
  92. 92. Stereotypes  Stereotypes are alias for annotations  An help to avoid annotations Hell  An other way to enable alterntives
  93. 93. Stereotypes  we have a lot of bean with @Conversation and @Named  we can create a stereotype gathering both @Stereotype @Named @ConversationScoped @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD}) @Documented public @interface Wizard { } We are delcaring a Stereotype All the annotations we want to put in the stereotype 1 2 2 1 2
  94. 94. Using Stereotypes  We can use the stereotype by replacing @Named @ConversationScoped public class ConferenceBean implements Serializable { ... }  by…​ @Wizard public class ConferenceBean implements Serializable { ... }
  95. 95. More decoupling with Events
  96. 96. Events in action public class FirstBean { @Inject Event<Post> postEvent; public void saveNewPost(Post myPost) { postEvent.fire(myPost); } } public class SecondBean { public void listenPost(@Observes Post post) { System.out.println("Received : " + evt.message()); } } Event<T> is injected at the firing side. T is the event payload type (here my Post class) When needed, we fire the event with our instance of T (here the Post object to save) At the consuming side, we define an observer for a given payload type with @Observes annotation. If observed type match fired event type, the observer is called. 1 2 3 1 2 3
  97. 97. Observer resolution mimics typesafe resolution (in a looser way) public class FirstBean { @Inject Event<Post> postEvent; public void saveNewPost(Post myPost) { postEvent.select(new AnnotationLiteral()<French> {}).fire(myPost); } } public class SecondBean { public void listenFrPost(@Observes @French Post post) {} public void listenPost(@Observes Post post) {} public void listenObject(@Observes Object obj) {} public void listenEnPost(@Observes @English Post post) {} } These observers will be triggered (empty subset is accepted for qualifiers when resolving observers) This observer won’t be triggered 1 1 1 2 1 2
  98. 98. Hooking on context lifecycle with events public class SecondBean { public void beginRequest(@Observes @Initialized(RequestScoped.class) ServletRequest req) {} public void endRequest(@Observes @Destroyed(RequestScoped.class) ServletRequest req) {} public void beginSession(@Observes @Initialized(SessionScoped.class) HttpSession session) {} public void endSession(@Observes @Destroyed(SessionScoped.class) HttpSession session) {} public void beginApplication(@Observes @Initialized(ApplicationScoped.class) ServlerContext ctx) {} public void endApplication(@Observes @Destroyed(ApplicationScoped.class) ServlerContext ctx) {} }  Observer must be defined in a bean  Observer resolution may trigger bean instance creation.  So observers above are a nice way to initailize bean with its context
  99. 99. Back to demo  BankingService is the observer  ReimburseService fires the event  The event is typed to Reimbursement
  100. 100. Introducing CDI Portable Extensions
  101. 101. Portable extensions  One of the most powerful feature of the CDI specification  Not really popularized, partly due to: 1. Their high level of abstraction 2. The good knowledge on Basic CDI and SPI 3. Lack of information (CDI is often reduced to a basic DI solution)
  102. 102. Extensions, what for?  To integrate 3rd party libraries, frameworks or legacy components  To change existing configuration or behavior  To extend CDI and Java EE  Thanks to them, Java EE can evolve between major releases
  103. 103. Extensions, how?  Observing SPI events at boot time related to the bean manager lifecycle  Checking what meta-data are being created  Modifying these meta-data or creating new ones
  104. 104. More concretely  Service provider of the service javax.enterprise.inject.spi.Extension declared in META-INF/services  Just put the fully qualified name of your extension class in this file import javax.enterprise.event.Observes; import javax.enterprise.inject.spi.Extension; public class CdiExtension implements Extension { void beforeBeanDiscovery(@Observes BeforeBeanDiscovery bbd) { } //... void afterDeploymentValidation(@Observes AfterDeploymentValidation adv) { } }
  105. 105. Internal Step Happen Once Loop on Elements Bean manager lifecycle Deployment Start Before Bean Discovery Scan Archive Process Annotated Type After Type Discovery Bean Eligibility Check Process Injection Point Process Injection Target Process Bean Attributes Process Bean Process Producer Process Observer Method After Bean Discovery After Deployment Validation Application Running Before Shutdown Undeploy Application
  106. 106. Example: Ignoring JPA entities  The following extension prevents CDI to manage entities  This is a commonly admitted good practice public class VetoEntity implements Extension { void vetoEntity(@Observes @WithAnnotations(Entity.class) ProcessAnnotatedType<?> pat) { pat.veto(); } }
  107. 107.  Extensions are launched during bootstrap and are based on CDI events  Once the application is bootstrapped, the Bean Manager is in read-only mode (no runtime bean registration)  You only have to @Observes built-in CDI events to create your extensions Remember
  108. 108. Understanding SPI to build CDI extensions
  109. 109. SPI can be split in 4 parts
  110. 110. SPI can be split in 4 parts  Type meta-model
  111. 111. SPI can be split in 4 parts  CDI meta-model
  112. 112. SPI can be split in 4 parts  CDI entry points
  113. 113. SPI can be split in 4 parts  SPI dedicated to extensions
  114. 114. Why having a type meta-model?  Because @Annotations are configuration  but they are also read-only  So to configure we need a mutable meta-model…​  …​for annotated types
  115. 115. SPI for type meta-model Annotated Type getBaseType() Set<Type> getTypeClosure() <T extends Annotation> getAnnotation(Class<T>) Set<Annotation> getAnnotations() boolean isAnnotationPresent(Class<? extends Annotation>) AnnotatedMember X Member getJavaMember() boolean isStatic() AnnotatedType<X> getDeclaringType() AnnotatedParameter X int getPosition() AnnotatedCallable<X> getDeclaringCallable() AnnotatedType X Class<X> getJavaClass() Set<AnnotatedConstructor<X>> getConstructors() Set<AnnotatedMethod<? super X>> getMethods() Set<AnnotatedField<? super X>> getFields() AnnotatedCallable X List<AnnotatedParameter<X>> getParameters() AnnotatedField X Field getJavaMember() AnnotatedConstructor X Constructor<X> getJavaMember() AnnotatedMethod X Method getJavaMember()
  116. 116. SPI dedicated to CDI meta-model BeanAttributes T Set<Type> getTypes() Set<Annotation> getQualifiers() Class<? extends Annotation> getScope() String getName() Set<Class<? extends Annotation>> getStereotypes() boolean isAlternative() Bean T Class<?> getBeanClass() Set<InjectionPoint> getInjectionPoints() boolean isNullable() Interceptor T Set<Annotation> getInterceptorBindings() boolean intercepts(InterceptionType type) Object intercept(InterceptionType, T, InvocationContext) Decorator T Type getDelegateType() Set<Annotation> getDelegateQualifiers() Set<Type> getDecoratedTypes() Producer T T produce(CreationalContext<T>) void dispose(T) Set<InjectionPoint> getInjectionPoints() InjectionTarget T void inject(T, CreationalContext<T>) void postConstruct(T) void preDestroy(T) InjectionPoint Type getType() Set<Annotation> getQualifiers() Bean<?> getBean() Member getMember() Annotated getAnnotated() boolean isDelegate() boolean isTransient() ObserverMethod T Class<?> getBeanClass() Type getObservedType() Set<Annotation> getObservedQualifiers() Reception getReception() TransactionPhase getTransactionPhase() void notify(T) EventMetadata Set<Annotation> getQualifiers() InjectionPoint getInjectionPoint() Type getType()
  117. 117. SPI providing CDI entry points Iterable T Iterator<T> iterator() void forEach() Spliterator<T> spliterator() Instance T T get() Instance<T> select() Instance<T> select(); boolean isUnsatisfied() boolean isAmbiguous() void destroy() CDI T Set<CDIProvider> discoveredProviders CDIProvider configuredProvider CDI<Object> current() void setCDIProvider(CDIProvider provider) BeanManager getBeanManager() BeanManager Object getReference(Bean<?>, Type, CreationalContext<?> ) Object getInjectableReference(InjectionPoint, CreationalContext<?> ) Set<Bean<?>> getBeans(Type, Annotation[]) Bean<? extends X> resolve(Set<Bean<? extends X>>) void validate(InjectionPoint) void fireEvent(Object, Annotation[]) boolean isQualifier(Class<? extends Annotation>) boolean isStereotype(Class<? extends Annotation>) boolean areQualifiersEquivalent(Annotation, Annotation) boolean areInterceptorBindingsEquivalent(Annotation, Annotation) Context getContext(Class<? extends Annotation>) ELResolver getELResolver() ExpressionFactory wrapExpressionFactory(ExpressionFactory) AnnotatedType<T> createAnnotatedType(Class<T>) InjectionTarget<T> createInjectionTarget(AnnotatedType<T>) InjectionTargetFactory<T> getInjectionTargetFactory(AnnotatedType<T>) BeanAttributes<T> createBeanAttributes(AnnotatedType<T>) Bean<T> createBean(BeanAttributes<T>, Class<X>, ProducerFactory<X>) InjectionPoint createInjectionPoint(AnnotatedField<?>) some methods skipped Unmanaged T Unmanaged(BeanManager, Class<T>) Unmanaged(Class<T>) UnmanagedInstance<T> newInstance() UnmanagedInstance T T get() UnmanagedInstance<T> produce() UnmanagedInstance<T> inject() UnmanagedInstance<T> postConstruct() UnmanagedInstance<T> preDestroy() UnmanagedInstance<T> dispose() provides
  118. 118. SPI dedicated to extensions BeforeBeanDiscovery addQualifier(Class<? extends Annotation>) addScope(Class<? extends Annotation>, boolean, boolean) addStereotype(Class<? extends Annotation>, Annotation[]) addInterceptorBinding(Class<? extends Annotation>, Annotation[]) addAnnotatedType(AnnotatedType<?>) AfterTypeDiscovery List<Class<?>> getAlternatives() List<Class<?>> getInterceptors() List<Class<?>> getDecorators() addAnnotatedType(AnnotatedType<?>, String) AfterDeploymentValidation BeforeShutdown AfterBeanDiscovery addBean(Bean<?>) addObserverMethod(ObserverMethod<?>) addContext(Context) AnnotatedType<T> getAnnotatedType(Class<T>, String) Iterable<AnnotatedType<T>> getAnnotatedTypes(Class<T>) ProcessAnnotatedType X AnnotatedType<X> getAnnotatedType() void setAnnotatedType(AnnotatedType<X>) veto() ProcessBean X Annotated getAnnotated() Bean<X> getBean() ProcessBeanAttributes T Annotated getAnnotated() BeanAttributes<T> getBeanAttributes() setBeanAttributes(BeanAttributes<T>) veto() ProcessInjectionPoint T, X InjectionPoint getInjectionPoint() setInjectionPoint(InjectionPoint) ProcessInjectionTarget X AnnotatedType<X> getAnnotatedType() InjectionTarget<X> getInjectionTarget() setInjectionTarget(InjectionTarget<X>) ProcessObserverMethod T, X AnnotatedMethod<X> getAnnotatedMethod() ObserverMethod<T> getObserverMethod() ProcessProducer T, X AnnotatedMember<T> getAnnotatedMember() Producer<X> getProducer() setProducer(Producer<X>)
  119. 119. All these SPI interfaces are events containing meta-model SPI  These events fired at boot time can only be observed in CDI extensions  For instance:  A ProcessAnnotatedType<T> event is fired for each type being discovered at boot time  Observing ProcessAnnotatedType<Foo> allows you to prevent Foo to be deployed as a bean by calling ProcessAnnotatedType#veto()
  120. 120.  We got this Javadoc: Back to demo
  121. 121. Back to demo We don’t have the code of the BillingService  Create a BillingServiceExtension which calls BillingService  Using observers BillingServiceObserver  Extension has to be enabled
  122. 122. CDI 2.0
  123. 123. About CDI 2.0  Work is in progress on CDI 2.0  Come see the session "CDI 2.0 is coming" thursday 2pm room 8  One of the new features is the asynchronous events  The good news is: "you can test it today"
  124. 124. Asynchronous event example public class FirstBean { @Inject Event<Post> postEvent; public void saveNewPost(Post myPost) { postEvent.fireAsync(myPost); } } public class SecondBean { public void listenPost(@ObservesAsync Post post) { System.out.println("Received : " + evt.message()); } } We introduced a fireAsync() method For backward compatibility we had to add an @ObservesAsync observer 1 2 1 2
  125. 125. Let’s test asynchronous event  Weld 3 Alpha (preview of CDI 2.0 RI) is available  WildFly includes a patch mechanism to upgrade implementations  We can patch WildFly with a simple command ./jboss-cli.sh --command="patch apply ~/wildfly-10.0.0.CR4-weld- 3.0.0.Alpha13-patch.zip"
  126. 126. Back to demo Let’s add asynchronous event
  127. 127. Conclusion
  128. 128. References  CDI Specification - cdi-spec.org  Code & slides repository - github.com/antoinesd/cdi-forge-uni  Slides generated with Asciidoctor, PlantUML and DZSlides backend  Original slide template - Dan Allen & Sarah White
  129. 129. Antoine Sabot-Durand Antonio Goncalves  @antoine_sd @agoncal

×