extending java ee with cdi and jboss forge
TRANSCRIPT
ExtendJavaEEwithCDI
AntoineSabot-Durand·AntonioGoncalves
AntoineSabot-Durand
RedHatCDIspeclead@antoine_sdnext-presso.comgithub.com/antoinesd
AntonioGoncalves
JavaChampionContractordevelopperantoniogoncalves.org@agoncalgithub.com/agoncal
Agenda
MeetExpensEEsMeetCDIBasicDIProducersInterceptorsQualifiersProgrammaticLookupContextsEventsExtensionsConclusion
ExpensesmadeEEsy
MeetExpensEEs
WewereaskedtobuildthisappAwebapptomanageexpensesBasedonJavaEE7CDI1.2JPA2.1JSF2.2JAX-RS2.0Abletointegratelegacylibs
Storyboard-HomeScreen
Storyboard-LoginScreen
Storyboard-NewUserScreen
Storyboard-LoggedIn
Storyboard-Profile
Storyboard-ExpenseDetail
Storyboard-ExpenseConfirm
Storyboard-ViewExpenses
Architecture-ModelUser
String loginString passwordString nameString emailUserRole role
Reimbursement
Date dateSet<Expense> expensesCurrency currencyUser userConference conference
Expense
descriptionDate dateFloat amountExpenseType expenseTypeprivate Currency currency
Conference
String nameDate dateString countryString city
1
*
1 *
1
*
Let’sForgeExpensEEs!
JBossForge
Forge
1. Generation&Scaffoldingtool2. Shellcommands&IDEIntegration3. Getsyoustartquickly4. Takescareofintegration5. Pluginbased6. JavaEE…Butanythingelsereally7. Moreinfoonforge.jboss.org/
Backtodemo RunJBossForge
CreateJPAEntities
ScaffoldJSFCRUDpages
ScaffoldRESTEndpoint
RunForgescript
ArchitectureForgehtml
web rest
model
ConferenceCRUDExpenseCRUD ReimbursementCRUD UserCRUD
ConferenceBeanExpenseBean ReimbursementBean UserBean ConferenceEndpoint
ConferenceExpenseService Reimbursement User
useuse use use
useuseuse use use
NotFinishedYet
1. OnlyaCRUDapp2. No"Createexpenses"Wizard3. NoLogin/logout4. Primefaces5. JavaEE6insteadofJavaEE76. JTA1.2@Transactional7. JPA2.1InsertdatainDB8. NotinfullCDIprogrammingmodel!
Backtodemo
Refactortheapp
Addaservicelayer
AddJavaEE7dependencies
LessEJBMoreCDI
WebjarforTwitterBootstrap
2hourslater…
NewArchitecturehtml
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
ContextsandDependencyInjection
MeetCDI
WhatisCDI?AJavaEEspec.ASL2(evenTCK)LaunchedinJavaEE62releasessincelaunch1.2islast(JavaEE7)2.0onitsway2majorimpl.JBossWeld(RI)ApacheOpenWebBeans
What’sincluded?CDIProvides:
1. Apowerfulprogrammingmodel2. DifferentLifecyclesforstateful
objects3. ATypesafeDImechanism4. Theabilitytodecorateinjected
objects5. Theabilitytointerceptmethods6. Aneventnotificationmodel7. ASPIallowingportableextensionstointegrate3rdpartytech
“CDIexplainedtoyourboss
CDIbringsapowerfulandmodernprogrammingmodelthatstandardizesgoodpractices.
CDIprovidesaconsistentandseamlesswaytointegrate3rdpartyframeworkstothismodel.
Onceadopted,CDIwillbecometheinvisiblebackboneofourprojects.
“
Onecontainertorulethemall ThecontaineristheheartofCDI
ThecontainerchecksallpossibleCDIcodeatboottime
Thecontainermanagesyourcomponentslifecycleandservices
Yet,you’llneverseeit(exceptinadvanceddevelopment)
Containerisyourapplication’sinvisibleconductor
@Inject
AlmostEverythingisabean
Duringboottime,thecontainerscanseachclasstocheckifitmeetstheconditionstobeabean
Theseconditionsareverylooseso,mostpojoclassesarebeans
public class HelloService { public String hello() { return "Hello World!"; }}
HelloService isaconcreteclasswithadefaultconstructor,thereforeit’sabean
1
1
Injectioninbeanfield Abeancaninjectotherbeansorbeinjectedintootherbeans
public class MyBean { @Inject private HelloService service;
public void displayHello() { display(service.hello(); }}
@Inject annotationdefinesaninjectionpoint(hereafield)
Thecontainerlooksaforthebeantoinjectbyitstype(here HelloService )
12
1
2
Injectioninbeanconstructor
Tobecomeabeanaclassmusthaveaconstructorwithnoparametersoraconstructorannotated @Inject
public class MyBean {
private HelloService service;
@Inject private MyBean(HelloService service) { this.service = service; }}
Onlyoneconstructorcanbeannotatedwith @Inject
Injectioninamethod Suchmethodiscalledinitializermethods
public class MyBean {
private HelloService service;
@Inject public void initService(HelloService service) { this.service = service; }}
allinitializermethodsarecalledatbeaninstantiation
TherecouldbeonlyoneEachinjectionpointischeckedatboottime:
Ifnobeaniseligibleforit,theinjectionpointisunsatisfied
Ifmultiplebeansareeligibleforit,theinjectionpointisambiguous
Inbothcasea DeploymentException isthrownbythecontainer
Backtodemo
InjectaLoggerinto UserService
Logamessagein persist method
Whenyoudon’townyourbean’sclass,use
Producers
Whatisaproducer?Awaytodeclareabean…
…fromafieldoramethod…
…foraclassyoudon’town…
…oranonCDIclass.
DeclaringaProducerpublic class ProducingBean { @Produces private List<Integer> mapInt = new ArrayList<>();
@Produces @French public List<String> FrenchListStrProducer() { List<String> res = Arrays.asList("bonjour"); return res; }}
ProducersshouldbedeclaredinaBeanclass
It’safieldoramethodannotatedwith @Produces (staticmethodssupported)
Producerscanhavequalifiers
valueoffieldormethodreturnedvalueisthebeaninstance
12
23
4
1
2
3
4
Producersareanotherkindofbean theycanhavequalifierslikeanyotherbean
producermethod(orfield)iscalledtocreatethebeaninstance
public class MyBean {
@Inject @French private List<String> frenchListStrBean;
@Inject public MyBean(List<Integer> initValues) { ... }}
DisposersareProducer’shousekeeping Disposerscanfreeresourceswhenproducedbeanisdestroyed
public class ProducingBean {
@Produces public MyNonCDIClass myProducer() { return new MyNonCdiClass(); }
public void releaseMyInstance(@Disposes MyNonCdiClass inst) { inst.clean(); }}
Willbecalledatthebeaninstanceendoflife
1
1
Producermethodscanhaveparameters Parameterswillbeinjectedbythecontaineratinstantiationtime
Theyshouldhavematchingbeansotherwisedeploymentfails
public class ProducingBean {
@Produces public MyNonCDIClass listStrProducer(MyBean bean) { ... }}
Thecontainerresolves MyBean andpassesittotheproducerwhenaninstanceofthebeanisrequested
1
1
Backtodemo
Theclass LoggerProducer producesaLogger
Injectingaloggerinservicedoesn’tchange
AccessingInjectionPointmetadata ThecontainercanprovidemetadataabouttheInjectionPoint
public class ProducingBean {
@Produces public Logger produceLog(InjectionPoint injectionPoint) { return Logger.getLogger(injectionPoint.getMember() .getDeclaringClass().getName()); }}
InjectionPoint isaSPIinterface.Thecontainercaninjectittogiveinfoabouttheinjectionpoint
1
1
Backtodemo
Add InjectionPoint APItotheproducer
Injectingaloggerinservicedoesn’tchange
InterceptorsandDecorators
InterceptorsvsDecorators TheyarebothAspectOrientedProgrammingtools
Interceptorsaretechnicaloriented:transaction,security,logging
Interceptorsareboundtoanybeanorbeanmethod
Decoratorsarebusinessoriented:changethebehaviourofabean
Decoratorsareboundtoabeantype
Interceptors Interceptorsaredefinedintheirownspecification
Usershouldcreateanannotationasaninterceptorbinding
Interceptorclassmustbeboundtoaninterceptorbinding
Interceptorclasscontainsinterceptingmethodswhichareboundtolifecyclephaseofinterceptedbean
Interceptorbindingisaddedtointerceptedbeanormethod
Interceptorbindingexample Aninterceptorbindingisannotationusedintwodifferentplaces
Ontheinterceptorandontheinterceptedelementtolinkthem
@InterceptorBinding @Target({METHOD, TYPE})@Retention(RUNTIME)public @interface Loggable {}
Thisannotationcomesfrominterceptorspecification.Itmakes @Loggable aninterceptorbinding
1
1
Interceptorexample@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()); } }}
Wemaketheclassaninterceptor( @Interceptor comesfromInterceptorspec)
Webindthisinterceptortothe @Loggable interceptorbinding
Weactivateandgiveaprioritytotheinterceptor(canbedonein beans.xml configfileaswell)
Thisinterceptingmethodwillbeinvokedinsteadoftheinterceptedone.
12
3
4
1
2
3
4
InterceptorUsage@Loggable public class MyBean { ... }
public class MyOtherBean {
@Loggable public String hello() { ... }
}
Allbean’smethodwillbeintercepted
Thismethodwillbeintercepted
1
2
1
2
Decorators Tobedecorated,abeanshouldimplementaninterface
Thedecoratorhastoimplementthesameinterface
Itisdeclaredwith @Decorator annotation
Itcanbeanabstractclass(letsyouchoosemethodstodecorate)
Itinjectsthedecoratedbeanwith @Delegate annotation
DecoratorExample@Decorator
@Priority(Interceptor.Priority.APPLICATION)
public abstract class HelloDecorator implements HelloService {
@Inject @Delegate HelloService service;
public String hello() { return service.hello() + "-decorated"; }}
Declaresthattheclassisadecorator
Activatesandgivesaprioritytothedecorator(couldbedonevia beans.xml file)
Decoratorscanbeabstractandshouldsharethesameinterfacethandecoratedbeans
Decoratedbeanisannotatedwith @Delegate (otherbeanscouldbeinjectedindecorator)
Decoratingmethodiscalledinsteadofdecoratedbeanmethod.Delegatecanbeusedinit.
12
3
4
5
1
2
3
4
5
Backtodemo Createa @Loggable interceptorbinding
Createa LoggingInterceptor
Add LoggingInterceptor toeachservice
Enableinterceptorinthe beans.xml
Solvingambiguouscaseswith
Qualifiers
Whenaninjectionpointresolvestomultiplebeans…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!"; }}
deploymentfailswithan Ambiguous dependencies error.
ThesolutionistocreateQualifiers…@Qualifier@Retention(RUNTIME)@Target({FIELD, TYPE, METHOD, PARAMETER})public @interface French {}
@Qualifier@Retention(RUNTIME)@Target({FIELD, TYPE, METHOD, PARAMETER})public @interface English {}
Qualifiersareannotationsannotatedwith @Qualifier
toqualifybeanssharingthesametype…
@Frenchpublic class FrenchHelloService implements HelloService { public String hello() { return "Bonjour tout le monde!"; }}
@Englishpublic class EnglishHelloService implements HelloService { public String hello() { return "Hello World!"; }}
anddistinguishthematinjectionpoints.
public class MyBean { @Inject @French HelloService serviceFr;
@Inject @English HelloService serviceEn; }
Qualifiersaretypesenhancinginjection,yetkeepingitstrongtyped
Qualifierscanhavemembers@Qualifier@Retention(RUNTIME)@Target({FIELD, TYPE, METHOD, PARAMETER})public @interface Language {
LangChoice value();
@Nonbinding String description() default "";
public enum LangChoice { FRENCH, ENGLISH }}
@Nonbinding member’svalueisn’tusedinbeanresolutionmechanism
1
1
Bindingmemberslimitthenumberofannotationstoaddtoyourcode
@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;}
Multiplequalifiers Abeancanhavemultiplequalifiers
@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 isaqualifier
1
1
Multiplequalifiers
Injectionpointcanhaveanonemptysubsetofthebean’squalifiers
@Language(FRENCH) @Consolepublic class FrenchHelloService implements HelloService { public String hello() { return "Bonjour tout le monde!"; }}
public class MyBean { @Inject @Console HelloService serviceFr;}
Multiplequalifiers Injectionpointcan’thaveasupersetofbean’squalifier
@Language(FRENCH) @Consolepublic class FrenchHelloService implements HelloService { public String hello() { return "Bonjour tout le monde!"; }}
public class MyBean { @Inject @Language(FRENCH) @Console @Language(CANADIAN) HelloService serviceFr;}
Unsatisfiedinjectionpoint:deploymentfails
1
1
Built-inqualifiers
@Named setbeannameforweaktypedenvironment(EL,Javascript)
@Default addedtobeanswithoutqualifierorhavingonly@Named
@Any addedtoallbeansforprogrammaticlookupanddecorators
@Intercepted & @Decorated toinjectinterceptedbeaninaninterceptor/Decorator
@Initialized toqualifyeventswhenacontextisstarted
Examplespublic class MyBean { ... } @Namedpublic class MyBean2 { ... }
@Named @Language(FRENCH) public class MyBean2 { @Inject MyBean2 bean;}
thisbeanhas @Default and @Any qualifiers
thisbeanhas @Default , @Named and @Any qualifiers
thisbeanhas @Language(FRENCH) , @Named and @Any qualifiers
thisinjectionpointhas @Default qualifier
1
2
3
4
1
2
3
4
BeanTypes Abeanhasasetoftypesdependingonitsdefinition
Beantypessetforamanagedbeancontainsthebeanclass,everysuperclassandallinterfacesitimplementsdirectlyorindirectly.
Beantypescanberestrictedbyusing @Typed annotation( Objectisalwaysintheset)
BeantypesExamplepublic 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
Parameterizedtypescount Parameterizedtypesinformationiskept(i.e.notypeerasure)
public class MyBean {
@Inject Service<User> userService;
@Inject Service<Staff> staffService; }
Injectionpointsabovecanbesatisfiedwithoutambiguity
PerformingTypesafeResolution Whenresolvingabeantobeinjectedtoaninjectionpoint…
…thecontainerconsidersbeantypeandqualifiers.
Ifthebeanhasabeantypethatmatchesinjectionpointtype…
…andthebeanhasallthequalifiersoftheinjectionpoint…
…thebeanisassignabletotheinjectionpoint.
Backtodemo Createa @Clear and @Encrypted qualifiers
Createa DigestPassword interface
Twoimplementations ClearPassword and EncryptPassword
Injectencryptedpassworddigestinto AccountBean andUserService
Resolvingbeansatruntime
ProgrammaticLookup
MeetInstance interface Instance interfaceletsperformtypesaferesolutionatruntime
public class MyBean {
@Inject Instance<HelloService> service;
public void displayHello() { display(service.get().hello()); }}
Instance<T> injectionpointsarealwayssatisfiedandneverfailatdeploymenttime
with Instance<T> youcontrolwhenbeanainstanceisrequestedwiththe get() method
1
2
1
2
Checkbeanexistenceatruntime Instance<T> containsmethodstotestbeanresolution
public class MyBean {
@Inject Instance<HelloService> service;
public void displayHello() { if (!(service.isUnsatisfied() || service.isAmbiguous())) { display(service.get().hello()); } }}
iftouskipthetest get() maythrowanexception
1
1
Loopingonallbeansinthe Instance
Instance<T> isiterable
It’swhereweusethe @Any qualifierpresentonallbeans
public class MyBean {
@Inject @Any Instance<HelloService> services;
public void displayHello() { for (HelloService service : services) { display(service.hello()); } }}
Selectabeanbyitsqualifier
AnnotationLiteral isahelperclasstocreateanannotationinstance
public class MyBean {
@Inject @Any Instance<HelloService> services;
public void displayHello() { display( services.select(new AnnotationLiteral()<French> {}).get()); }}
select() alsoacceptsatype.
youcanuse TypeLiteral tocreateinstancesofparameterizedtypes
1
1
Backtodemo Loopthroughall DigestPassword
Thanksto @Any
Useeither @Encrypted or @Clear
Beanslifeanddeath
Contexts
Bean,ScopeandContexts AllBeanshaveascopedefinedbyanannotation
Eachscopeisassociatedtoacontextobject
Soeachbeanisboundtoacontext
Thecontextisinchargeofcreatingbeaninstances
TheContainerisinchargeofcreatinganddestroyingcontexts
Availablescopes
CDIprovidesthefollowingbuilt-inscopes(andassociatedcontexts):
@Dependent (default)beanhasthesamescopethanitsinjector @ApplicationScoped instanceislinkedtoapplicationlifecycle @SessionScoped instanceislinkedtohttpsessionlifecycle @RequestScoped instanceislikedtohttprequestlifecycle @ConversationScoped lifecyclemanuallycontrolledwithinsession
Instanceiscreatedatfirstrequestanddestroyedwithitsboundcontext
CDISPIallowsthirdpartytocreatetheircustomcontexts
Examplespublic class BaseHelloService implements HelloService { ... }
@RequestScoped public class RequestService { @Inject HelloService service;}
@ApplicationScoped public class ApplicationService { @Inject RequestService service; }
Beanhasdefaultscope @Dependent ,instancesarecreatedforeachinjection
Beanis @RequestScoped .Instanceiscreatedbyrequestcontextanddestroyedwithrequestcontext
Beanis @ApplicationScoped .Instanceiscreatedbyapplicationcontextandwillliveduringallapplication
Noproblemtoinjectbeanfromanotherscope:CDIwillprovidetherightbean
1
2
3
4
1
2
3
4
Goodtoknow
instancesarecreatedwhenfirstaccessednotwiththeircontext
Abeaninstanceisasingletoninitscontext
WithSPIyoucanmanuallydestroyaninstanceinacontext
Acontextcanbeinactivewithoutbeingdestroyed
Requestcontextismorethanamappingto ServletRequest lifecycle
Moreon@ConversationScoped Acontextwhoselifecycleiscontrolledbythedev
Conversationistransientorlongrunning
It’spromotedtolongrunningwith Conversation built-inbean…
…bycalling begin() method…
…andterminatedwith end() .
TherecanbemultipleconversationsinoneHttpSession
Conversation id isusedtoretrievetheactiveconversation
Example@ConversationScopedpublic class WizardBean {
@Inject Conversation conversation;
public startWizard() { ... conversation.begin() ... }
public endWizard() { ... conversation.end() ... }}
Backtodemo Wizardtocreatenewexpenses
Useof @ConversationScoped
JSFdealswith cid
Contextsarenotalwaysconvenient
Lifecyclemagicmanagementcanlimityou
Beaninstancesaredestroyedonlywhencontextends
Andyoumightwanttogetridofinstancebutkeepthecontext
CDI1.1addedaprogrammaticwaytodestroyaninstance
Example-Logoutandsessioninvalidation@Inject FacesContext facesContext;
public String logout(){ try{ facesContext.getExternalContext().invalidateSession(); return "success?faces-redirect=true"; } catch(Exception e){ return "error"; }}
redirectisimportanttoendcurrentrequestandkillthesessioneffectively
wemightwanttokeepthecurrentsessionandlogoutuser
notagoodpracticetocallUIlayerfromservicetoperformbusiness
1
1
Example-DologoutinaCDIway@Named@SessionScopedpublic class AccountBean implements Serializable {
@Inject private Instance<AccountBean> myInstance;
public String doLogout() { myInstance.destroy(myInstance.get()); return "/index"; }
SinceCDI1.1, Instance providesa destroy() methodtoremoveaninstancefromitscontext
1
1
Backtodemo
AccountBean injectsareferencetoitself
AccountBean destroysitforlogout
Alternatives&Stereotypes
Alternatives
Analternativeisabeanthatmustbeexplicitlyselected
Whenselecteditsuperseedsbeanssharingthesametype
AconvenientwaytocreateMockorspecificimplementation
Analternativecanbeactivatedin beans.xml orwith @Priority
AlternativeExamplepublic SapService implements ErpService { }
@Alternativepublic MockSapService implements ErpService { }
@ApplicationScopedpublic OrderService { @Inject ErpService service; }
Aservicedoingheavyorcostlyoperation
Canbemockedwithanalternative
Beansinjectingtheservicehavenothingtodo.Alternativewillreplacetheoriginalbeanifselected
1
2
3
1
2
3
SelectingtheAlternative
With @Priority annotaion(forthewholeapp)
@Alternative@Priority(Interceptor.Priority.APPLICATION) public MockSapService implements ErpService {}
With beans.xml file(forthecurrentmodule)
<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
Stereotypes
Stereotypesarealiasforannotations
AnhelptoavoidannotationsHell
Anotherwaytoenablealterntives
Stereotypes
wehavealotofbeanwith @Conversation and @Named
wecancreateastereotypegatheringboth
@Stereotype @Named @ConversationScoped @Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})@Documentedpublic @interface Wizard {}
WearedelcaringaStereotype
Alltheannotationswewanttoputinthestereotype
12
2
1
2
UsingStereotypes
Wecanusethestereotypebyreplacing
@Named @ConversationScopedpublic class ConferenceBean implements Serializable {...}
by…
@Wizardpublic class ConferenceBean implements Serializable {...}
Moredecouplingwith
Events
Eventsinactionpublic 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> isinjectedatthefiringside. T istheeventpayloadtype(heremy Post class)
Whenneeded,wefiretheeventwithourinstanceof T (herethe Post objecttosave)
Attheconsumingside,wedefineanobserverforagivenpayloadtypewith @Observes annotation.Ifobservedtypematchfiredeventtype,theobserveriscalled.
1
2
3
1
2
3
Observerresolutionmimicstypesaferesolution(inalooserway)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) {} }
Theseobserverswillbetriggered(emptysubsetisacceptedforqualifierswhenresolvingobservers)
Thisobserverwon’tbetriggered
11
12
1
2
Hookingoncontextlifecyclewitheventspublic 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) {}}
Observermustbedefinedinabean
Observerresolutionmaytriggerbeaninstancecreation.
Soobserversaboveareanicewaytoinitailizebeanwithitscontext
Backtodemo BankingService istheobserver
ReimburseService firestheevent
Theeventistypedto Reimbursement
IntroducingCDIPortableExtensions
Portableextensions
OneofthemostpowerfulfeatureoftheCDIspecification
Notreallypopularized,partlydueto:
1. Theirhighlevelofabstraction2. ThegoodknowledgeonBasicCDIandSPI3. Lackofinformation(CDIisoftenreducedtoabasicDIsolution)
Extensions,whatfor?
Tointegrate3rdpartylibraries,frameworksorlegacycomponents
Tochangeexistingconfigurationorbehavior
ToextendCDIandJavaEE
Thankstothem,JavaEEcanevolvebetweenmajorreleases
Extensions,how?
ObservingSPIeventsatboottimerelatedtothebeanmanagerlifecycle
Checkingwhatmeta-dataarebeingcreated
Modifyingthesemeta-dataorcreatingnewones
Moreconcretely
Serviceprovideroftheservicejavax.enterprise.inject.spi.Extension declaredinMETA-INF/services
Justputthefullyqualifiednameofyourextensionclassinthisfile
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) { }}
Internal Step Happen Once Loop on Elements
Beanmanagerlifecycle
DeploymentStart
BeforeBean
DiscoveryScan
Archive
ProcessAnnotated
Type
AfterType
Discovery
BeanEligibility
Check
ProcessInjection
Point
ProcessInjectionTarget
ProcessBean
Attributes
ProcessBean
ProcessProducer
ProcessObserverMethod
AfterBean
Discovery
AfterDeploymentValidation
ApplicationRunning
BeforeShutdown
UndeployApplication
Example:IgnoringJPAentities
ThefollowingextensionpreventsCDItomanageentities
Thisisacommonlyadmittedgoodpractice
public class VetoEntity implements Extension {
void vetoEntity(@Observes @WithAnnotations(Entity.class) ProcessAnnotatedType<?> pat) { pat.veto(); }}
ExtensionsarelaunchedduringbootstrapandarebasedonCDIevents
Oncetheapplicationisbootstrapped,theBeanManagerisinread-onlymode(noruntimebeanregistration)
Youonlyhaveto@Observes built-inCDIeventstocreateyourextensions
Remember
UnderstandingSPItobuildCDIextensions
SPIcanbesplitin4parts
SPIcanbesplitin4parts
Typemeta-model
SPIcanbesplitin4parts
CDImeta-model
SPIcanbesplitin4parts
CDIentrypoints
SPIcanbesplitin4parts
SPIdedicatedtoextensions
Whyhavingatypemeta-model?
Because @Annotations areconfiguration
buttheyarealsoread-only
Sotoconfigureweneedamutablemeta-model…
…forannotatedtypes
SPIfortypemeta-model
Annotated
Type getBaseType()Set<Type> getTypeClosure()<T extends Annotation> getAnnotation(Class<T>)Set<Annotation> getAnnotations()boolean isAnnotationPresent(Class<? extends Annotation>)
AnnotatedMemberX
Member getJavaMember()boolean isStatic()AnnotatedType<X> getDeclaringType()
AnnotatedParameterX
int getPosition()AnnotatedCallable<X> getDeclaringCallable()
AnnotatedTypeX
Class<X> getJavaClass()Set<AnnotatedConstructor<X>> getConstructors()Set<AnnotatedMethod<? super X>> getMethods()Set<AnnotatedField<? super X>> getFields()
AnnotatedCallableX
List<AnnotatedParameter<X>> getParameters()
AnnotatedFieldX
Field getJavaMember()
AnnotatedConstructorX
Constructor<X> getJavaMember()
AnnotatedMethodX
Method getJavaMember()
SPIdedicatedtoCDImeta-model
BeanAttributesT
Set<Type> getTypes()Set<Annotation> getQualifiers()Class<? extends Annotation> getScope()String getName()Set<Class<? extends Annotation>> getStereotypes()boolean isAlternative()
BeanT
Class<?> getBeanClass()Set<InjectionPoint> getInjectionPoints()boolean isNullable()
InterceptorT
Set<Annotation> getInterceptorBindings()boolean intercepts(InterceptionType type)Object intercept(InterceptionType, T, InvocationContext)
DecoratorT
Type getDelegateType()Set<Annotation> getDelegateQualifiers()Set<Type> getDecoratedTypes()
ProducerT
T produce(CreationalContext<T>)void dispose(T)Set<InjectionPoint> getInjectionPoints()
InjectionTargetT
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()
ObserverMethodT
Class<?> getBeanClass()Type getObservedType()Set<Annotation> getObservedQualifiers()Reception getReception()TransactionPhase getTransactionPhase()void notify(T)
EventMetadata
Set<Annotation> getQualifiers()InjectionPoint getInjectionPoint()Type getType()
SPIprovidingCDIentrypoints
IterableT
Iterator<T> iterator()void forEach()Spliterator<T> spliterator()
InstanceT
T get()Instance<T> select()Instance<T> select();boolean isUnsatisfied()boolean isAmbiguous()void destroy()
CDIT
Set<CDIProvider> discoveredProvidersCDIProvider 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
UnmanagedT
Unmanaged(BeanManager, Class<T>)Unmanaged(Class<T>)UnmanagedInstance<T> newInstance()
UnmanagedInstanceT
T get()UnmanagedInstance<T> produce()UnmanagedInstance<T> inject()UnmanagedInstance<T> postConstruct()UnmanagedInstance<T> preDestroy()UnmanagedInstance<T> dispose()
provides
SPIdedicatedtoextensions
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>)
ProcessAnnotatedTypeX
AnnotatedType<X> getAnnotatedType()void setAnnotatedType(AnnotatedType<X>)veto()
ProcessBeanX
Annotated getAnnotated()Bean<X> getBean()
ProcessBeanAttributesT
Annotated getAnnotated()BeanAttributes<T> getBeanAttributes()setBeanAttributes(BeanAttributes<T>)veto()
ProcessInjectionPointT, X
InjectionPoint getInjectionPoint()setInjectionPoint(InjectionPoint)
ProcessInjectionTargetX
AnnotatedType<X> getAnnotatedType()InjectionTarget<X> getInjectionTarget()setInjectionTarget(InjectionTarget<X>)
ProcessObserverMethodT, X
AnnotatedMethod<X> getAnnotatedMethod()ObserverMethod<T> getObserverMethod()
ProcessProducerT, X
AnnotatedMember<T> getAnnotatedMember()Producer<X> getProducer()setProducer(Producer<X>)
AlltheseSPIinterfacesareeventscontainingmeta-modelSPI
TheseeventsfiredatboottimecanonlybeobservedinCDIextensions
Forinstance:
A ProcessAnnotatedType<T> eventisfiredforeachtypebeingdiscoveredatboottime
Observing ProcessAnnotatedType<Foo>allowsyoutoprevent Foo tobedeployedasabeanbycallingProcessAnnotatedType#veto()
WegotthisJavadoc: Backtodemo
Backtodemo Wedon’thavethecodeofthe BillingService
Createa BillingServiceExtension whichcallsBillingService
Usingobservers BillingServiceObserver
Extensionhastobeenabled
CDI2.0
AboutCDI2.0
WorkisinprogressonCDI2.0
Comeseethesession"CDI2.0iscoming"thursday2pmroom8
Oneofthenewfeaturesistheasynchronousevents
Thegoodnewsis:"youcantestittoday"
Asynchronouseventexamplepublic 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()); }}
Weintroduceda fireAsync() method
Forbackwardcompatibilitywehadtoaddan @ObservesAsync observer
1
2
1
2
Let’stestasynchronousevent
Weld3Alpha(previewofCDI2.0RI)isavailable
WildFlyincludesapatchmechanismtoupgradeimplementations
WecanpatchWildFlywithasimplecommand
./jboss-cli.sh --command="patch apply ~/wildfly-10.0.0.CR4-weld-3.0.0.Alpha13-patch.zip"
Backtodemo
Let’saddasynchronousevent
Conclusion
References
CDISpecification-cdi-spec.org
Code&slidesrepository-github.com/antoinesd/cdi-forge-uni
SlidesgeneratedwithAsciidoctor,PlantUMLandDZSlidesbackend
Originalslidetemplate-DanAllen&SarahWhite
AntoineSabot-DurandAntonioGoncalves@antoine_sd@agoncal