Top Banner
Going further with CDI 1.2 Antoine Sabot-Durand · Antonin Stefanutti
114

Going further with CDI

Jul 16, 2015

Download

Technology

Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Going further with CDI

GoingfurtherwithCDI1.2

AntoineSabot-Durand·AntoninStefanutti

Page 2: Going further with CDI

AntoninStefanutti

[email protected]/astefanutti

Slidesavailableatgithub.com/astefanutti/further-cdi

Page 3: Going further with CDI

CDI@Murex

CDIastheproductivityecosystemtobuildconnectivityinterfaces

Page 4: Going further with CDI

CDI@Murex

Page 5: Going further with CDI

AntoineSabot-Durand

SeniorSoftwareEngineerCDIco-speclead,JavaEE8EGRedHat,Inc.@antoine_sdwww.next-presso.comgithub.com/antoinesd

Page 6: Going further with CDI

ShouldIstayorshouldIgo?

AtalkaboutadvancedCDI

Mightbehardforbeginners

Don’tneedtobeaCDIguru

Page 7: Going further with CDI

@Inject @ProducesEvent<T> @Observes@Qualifier InjectionPoint

ShouldIstayorshouldIgo?

Ifyouknowthemostoftheseyoucanstay

Page 8: Going further with CDI

Moreconcretely

What’sincluded:

1. Realusecasesfromreallifewithrealusers2. Newapproachtointroduceportableextensionconcepts3. CodeinIDEwithtests

What’snotincluded:

1. IntroductiontoCDI2. Oldcontentonextension3. WorkwithContext(need2morehours)

Page 9: Going further with CDI

Toolsusedinthecode1/2

ApacheDeltaspike

1. ApacheDeltaSpikeisagreatCDItoolbox

2. Providehelperstodevelopextension3. Andacollectionofmoduleslike:

1. Security2. Data3. Scheduler

4. Moreinfoondeltaspike.apache.org

Page 10: Going further with CDI

Toolsusedinthecode2/2

Arquillian

1. Arquillianisanintegrationtestplatform2. ItintegrateswithJUnit3. Createyourdeploymentinadedicatedmethod

4. Andlaunchyourtestsagainstthecontainerofyourchoice

5. We’llusethe weld-se-embedded andweld-ee-embedded container

6. TherightsolutiontotestJavaEEcode7. Moreinfoonarquillian.org

Page 11: Going further with CDI

Agenda

MeetCDISPIIntroducingCDIExtensionsMetricsCDICDIQuizzCamelCDI

Slidesavailableatastefanutti.github.io/further-cdi

Page 12: Going further with CDI

MeetCDISPI

Page 13: Going further with CDI

SPIcanbesplitin4parts

Page 14: Going further with CDI

SPIcanbesplitin4parts

Typemeta-model

Page 15: Going further with CDI

SPIcanbesplitin4parts

CDImeta-model

Page 16: Going further with CDI

SPIcanbesplitin4parts

CDIentrypoints

Page 17: Going further with CDI

SPIcanbesplitin4parts

SPIdedicatedtoextensions

Page 18: Going further with CDI

Whyhavingatypemeta-model?

Because @Annotations areconfiguration

buttheyarealsoread-only

Sotoconfigureweneedamutablemeta-model…

… forannotatedtypes

Page 19: Going further with CDI

SPIfortypemeta-model

Annotated

TypegetBaseType()Set<Type>getTypeClosure()<TextendsAnnotation>getAnnotation(Class<T>)Set<Annotation>getAnnotations()booleanisAnnotationPresent(Class<?extendsAnnotation>)

AnnotatedMemberX

MembergetJavaMember()booleanisStatic()AnnotatedType<X>getDeclaringType()

AnnotatedParameterX

intgetPosition()AnnotatedCallable<X>getDeclaringCallable()

AnnotatedTypeX

Class<X>getJavaClass()Set<AnnotatedConstructor<X>>getConstructors()Set<AnnotatedMethod<?superX>>getMethods()Set<AnnotatedField<?superX>>getFields()

AnnotatedCallableX

List<AnnotatedParameter<X>>getParameters()

AnnotatedFieldX

FieldgetJavaMember()

AnnotatedConstructorX

Constructor<X>getJavaMember()

AnnotatedMethodX

MethodgetJavaMember()

Page 20: Going further with CDI

SPIdedicatedtoCDImeta-modelBeanAttributes

T

Set<Type>getTypes()Set<Annotation>getQualifiers()Class<?extendsAnnotation>getScope()StringgetName()Set<Class<?extendsAnnotation>>getStereotypes()booleanisAlternative()

BeanT

Class<?>getBeanClass()Set<InjectionPoint>getInjectionPoints()booleanisNullable()

InterceptorT

Set<Annotation>getInterceptorBindings()booleanintercepts(InterceptionTypetype)Objectintercept(InterceptionType,T,InvocationContext)

DecoratorT

TypegetDelegateType()Set<Annotation>getDelegateQualifiers()Set<Type>getDecoratedTypes()

ProducerT

Tproduce(CreationalContext<T>)voiddispose(T)Set<InjectionPoint>getInjectionPoints()

InjectionTargetT

voidinject(T,CreationalContext<T>)voidpostConstruct(T)voidpreDestroy(T)

InjectionPoint

TypegetType()Set<Annotation>getQualifiers()Bean<?>getBean()MembergetMember()AnnotatedgetAnnotated()booleanisDelegate()booleanisTransient()

ObserverMethodT

Class<?>getBeanClass()TypegetObservedType()Set<Annotation>getObservedQualifiers()ReceptiongetReception()TransactionPhasegetTransactionPhase()voidnotify(T)

EventMetadata

Set<Annotation>getQualifiers()InjectionPointgetInjectionPoint()TypegetType()

Page 21: Going further with CDI

ThisSPIcanbeusedinyourcode(1/2)

InjectionPoint canbeusedtogetinfoaboutwhat’sbeinginjected

@Qualifier@Retention(RetentionPolicy.RUNTIME)public@interfaceHttpParam{@NonbindingpublicStringvalue();}

@Produces@HttpParam("")StringgetParamValue(InjectionPointip,HttpServletRequestreq){returnreq.getParameter(ip.getAnnotated().getAnnotation(HttpParam.class).value());}

@Inject@HttpParam("productId")StringproductId;

Page 22: Going further with CDI

ThisSPIcanbeusedinyourcode(2/2)

InjectionPoint containsinfoaboutrequestedtypeat @Inject

classMyMapProducer(){

@Produces<K,V>Map<K,V>produceMap(InjectionPointip){if(valueIsNumber(((ParameterizedType)ip.getType())))returnnewTreeMap<K,V>();returnnewHashMap<K,V>();}

booleanvalueIsNumber(ParameterizedTypetype){Class<?>valueClass=(Class<?>)type.getActualTypeArguments()[1];returnNumber.class.isAssignableFrom(valueClass)}}

Page 23: Going further with CDI

SPIprovidingCDIentrypoints

UnmanagedT

Unmanaged(BeanManager,Class<T>)Unmanaged(Class<T>)UnmanagedInstance<T>newInstance()

UnmanagedInstanceT

Tget()UnmanagedInstance<T>produce()UnmanagedInstance<T>inject()UnmanagedInstance<T>postConstruct()UnmanagedInstance<T>preDestroy()UnmanagedInstance<T>dispose()

CDIProvider

CDI<Object>getCDI()

BeanManager

ObjectgetReference(Bean<?>,Type,CreationalContext<?>)ObjectgetInjectableReference(InjectionPoint,CreationalContext<?>)Set<Bean<?>>getBeans(Type,Annotation[])Bean<?extendsX>resolve(Set<Bean<?extendsX>>)voidvalidate(InjectionPoint)voidfireEvent(Object,Annotation[])

booleanisQualifier(Class<?extendsAnnotation>)booleanisStereotype(Class<?extendsAnnotation>)booleanareQualifiersEquivalent(Annotation,Annotation)booleanareInterceptorBindingsEquivalent(Annotation,Annotation)ContextgetContext(Class<?extendsAnnotation>)ELResolvergetELResolver()ExpressionFactorywrapExpressionFactory(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>)InjectionPointcreateInjectionPoint(AnnotatedField<?>)

somemethodsskiped

CDIT

Set<CDIProvider>discoveredProvidersCDIProviderconfiguredProvider

CDI<Object>current()voidsetCDIProvider(CDIProviderprovider)BeanManagergetBeanManager()

Page 24: Going further with CDI

SPIdedicatedtoextensions

BeforeBeanDiscovery

addQualifier(Class<?extendsAnnotation>)addScope(Class<?extendsAnnotation>,boolean,boolean)addStereotype(Class<?extendsAnnotation>,Annotation[])addInterceptorBinding(Class<?extendsAnnotation>,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()voidsetAnnotatedType(AnnotatedType<X>)veto()

ProcessBeanX

AnnotatedgetAnnotated()Bean<X>getBean()

ProcessBeanAttributesT

AnnotatedgetAnnotated()BeanAttributes<T>getBeanAttributes()setBeanAttributes(BeanAttributes<T>)veto()

ProcessInjectionPointT,X

InjectionPointgetInjectionPoint()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>)

Page 25: Going further with CDI

AlltheseSPIinterfacesareeventscontainingmeta-modelSPI

TheseeventsfiredatboottimecanonlybeobservedinCDIextensions

Forinstance:

A ProcessAnnotatedType<T> eventisfiredforeachtypebeingdiscoveredatboottime

Observing ProcessAnnotatedType<Foo>allowsyoutoprevent Foo tobedeployedasabeanbycallingProcessAnnotatedType#veto()

Page 26: Going further with CDI

IntroducingCDIPortableExtensions

Page 27: Going further with CDI

Portableextensions

OneofthemostpowerfulfeatureoftheCDIspecification

Notreallypopularized,partlydueto:

1. Theirhighlevelofabstraction2. ThegoodknowledgeonBasicCDIandSPI3. Lackofinformation(CDIisoftenreducedtoabasicDIsolution)

Page 28: Going further with CDI

Extensions,whatfor?

Tointegrate3rdpartylibraries,frameworksorlegacycomponents

Tochangeexistingconfigurationorbehavior

ToextendCDIandJavaEE

Thankstothem,JavaEEcanevolvebetweenmajorreleases

Page 29: Going further with CDI

Extensions,how?

ObservingSPIeventsatboottimerelatedtothebeanmanagerlifecycle

Checkingwhatmeta-dataarebeingcreated

Modifyingthesemeta-dataorcreatingnewones

Page 30: Going further with CDI

Moreconcretely

Serviceprovideroftheservicejavax.enterprise.inject.spi.Extension declaredinMETA-INF/services

Justputthefullyqualifiednameofyourextensionclassinthisfile

importjavax.enterprise.event.Observes;importjavax.enterprise.inject.spi.Extension;

publicclassCdiExtensionimplementsExtension{

voidbeforeBeanDiscovery(@ObservesBeforeBeanDiscoverybbd){}//...

voidafterDeploymentValidation(@ObservesAfterDeploymentValidationadv){}}

Page 31: Going further with CDI

InternalStep HappenOnce LooponElements

Beanmanagerlifecycle

DeploymentStart

BeforeBean

Discovery

ScanArchive

ProcessAnnotated

Type

AfterType

Discovery

BeanEligibility

Check

ProcessInjectionPoint

ProcessInjectionTarget

ProcessBean

Attributes

ProcessBean

ProcessProducer

ProcessObserverMethod

AfterBean

Discovery

AfterDeploymentValidation

ApplicationRunning

BeforeShutdown

UndeployApplication

Page 32: Going further with CDI

Example:IgnoringJPAentities

ThefollowingextensionpreventsCDItomanageentities

Thisisacommonlyadmittedgoodpractice

publicclassVetoEntityimplementsExtension{

voidvetoEntity(@Observes@WithAnnotations(Entity.class)ProcessAnnotatedType<?>pat){pat.veto();}}

Page 33: Going further with CDI

ExtensionsarelaunchedduringbootstrapandarebasedonCDIevents

Oncetheapplicationisbootstrapped,theBeanManagerisinread-onlymode(noruntimebeanregistration)

Youonlyhaveto@Observes built-inCDIeventstocreateyourextensions

Remember

Page 34: Going further with CDI

Howtointegratea3rdpartyLibrary(DropwizardMetrics)intotheCDIProgrammingModel

3rdpartyLibrary

Page 35: Going further with CDI

AboutDropwizardMetrics

Providesdifferentmetrictypes: Counter , Gauge , Meter , Timer ,…

Providesdifferentreporter:JMX,console,SLF4J,CSV,servlet,…

Providesa MetricRegistry whichcollectsallyourappmetrics

ProvidesannotationsforAOPframeworks: @Counted , @Timed ,…

… butdoesnotincludeintegrationwiththeseframeworks

Moreatdropwizard.github.io/metrics

Page 36: Going further with CDI

DiscoverhowwecreatedCDIintegrationmoduleforMetrics

Page 37: Going further with CDI

Metricsoutofthebox(withoutCDI)classMetricsHelper{publicstaticMetricRegistryregistry=newMetricRegistry();}

classTimedMethodClass{

voidtimedMethod(){Timertimer=MetricsHelper.registry.timer("timer");Timer.Contexttime=timer.time();try{/*...*/}finally{time.stop();}}}

Notethatif Timer called "timer" doesn’texist, MetricRegistry willcreateadefaultoneandregisterit

1

1

Page 38: Going further with CDI

BasicCDIintegrationclassMetricRegistryBean{@Produces@ApplicationScopedMetricRegistryregistry=newMetricRegistry();}

classTimedMethodBean{@InjectMetricRegistryregistry;

voidtimedMethod(){Timertimer=registry.timer("timer");Timer.Contexttime=timer.time();try{/*...*/}finally{time.stop();}}}

WecouldhavealotmorewithadvancedCDIfeatures

Page 39: Going further with CDI

Ourgoals1. ApplyametricwiththeprovidedannotationinAOPstyle

@Timed("timer")voidtimedMethod(){//...}

2. Registerautomaticallyproducedcustommetrics@Produces@Metric(name="myTimer")Timertimer=newTimer(newSlidingTimeWindowReservoir(1L,TimeUnit.MINUTES));//...@Timed("myTimer")voidtimedMethod(){/*...*/}

AnnotationsprovidedbyMetrics

1

1

1

1

Page 40: Going further with CDI

StepstoapplyatimerinAOPstyle

Createaninterceptorforthetimertechnicalcode

MaketheMetricsannotation @Timed avalidinterceptorbindingannotation

Programmaticallyadd @Timed asaninterceptorbinding

Usethemagic

Page 41: Going further with CDI

Preparinginterceptorcreation

Tocreateaninterceptorweshouldstartbydetectingthe"technicalcode"thatwillwrapthe"businesscode"

classTimedMethodBean{

@InjectMetricRegistryregistry;

voidtimedMethod(){Timertimer=registry.timer("timer");Timer.Contexttime=timer.time();try{//Businesscode}finally{time.stop();}}}

Page 42: Going further with CDI

Creatingtheinterceptor

Interceptorisanindependentspecification(JSR318).Highlightedcodebelowispartofit.

@InterceptorclassTimedInterceptor{

@InjectMetricRegistryregistry;@AroundInvokeObjecttimeMethod(InvocationContextcontext)throwsException{Timertimer=registry.timer(context.getMethod().getAnnotation(Timed.class).name());Timer.Contexttime=timer.time();try{

returncontext.proceed();}finally{time.stop();}}}

InCDIaninterceptorisabean,youcaninjectotherbeansinit

Herethe"business"oftheapplicationiscalled.Allthecodearoundisthetechnicalone.

1

2

1

2

Page 43: Going further with CDI

Activatingtheinterceptor@Interceptor@Priority(Interceptor.Priority.LIBRARY_BEFORE)classTimedInterceptor{

@InjectMetricRegistryregistry;

@AroundInvokeObjecttimeMethod(InvocationContextcontext)throwsException{Timertimer=registry.timer(context.getMethod().getAnnotation(Timed.class).name());Timer.Contexttime=timer.time();try{returncontext.proceed();}finally{time.stop();}}}

Givinga @Priority toaninterceptoractivatesit.ThisannotationispartoftheCommonAnnotationsspecification(JSR250).InCDI,interceptoractivationcanalsobedoneinthe beans.xml file.

1

1

Page 44: Going further with CDI

Addabindingtotheinterceptor@Timed@Interceptor@Priority(Interceptor.Priority.LIBRARY_BEFORE)classTimedInterceptor{

@InjectMetricRegistryregistry;

@AroundInvokeObjecttimeMethod(InvocationContextcontext)throwsException{Timertimer=registry.timer(context.getMethod().getAnnotation(Timed.class).name());Timer.Contexttime=timer.time();try{returncontext.proceed();}finally{time.stop();}}}

We’lluseMetrics @Timed annotationasinterceptorbinding

1

1

Page 45: Going further with CDI

Backoninterceptorbinding

Aninterceptorbindingisanannotationusedin2kindofplaces:

1. Ontheinterceptordefinitionstoassociatethemtothisannotation2. Onthemethods/classestobeinterceptedbythisinterceptor

Aninterceptorbindingshouldbeannotatedwiththe@InterceptorBinding metaannotationorshouldbedeclaredasaninterceptorbindingprogrammatically

Iftheinterceptorbindingannotationhasmembers:

1. Theirvaluesaretakenintoaccounttodistinguishtwoinstances2. Unlessmembersareannotatedwith @NonBinding

Page 46: Going further with CDI

@Timed sourcecodetellsusit’snotaninterceptorbinding

@Documented@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE,ElementType.CONSTRUCTOR,ElementType.METHOD,ElementType.ANNOTATION_TYPE})

public@interfaceTimed{

Stringname()default"";

booleanabsolute()defaultfalse;}

Lackof @InterceptorBinding annotationandwehavenocodetoadditprogrammatically

Noneofthemembershavethe @NonBinding annotationsothey’llbeusedtodistinguishtwoinstances(i.e.@Timed(name="timer1") and @Timed(name="timer2") willbe2differentinterceptorbindings)

1

2

2

1

2

Page 47: Going further with CDI

Theneeded @Timed sourcecodetomakeitaninterceptorbinding

@Documented@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE,ElementType.CONSTRUCTOR,ElementType.METHOD,ElementType.ANNOTATION_TYPE})@InterceptorBindingpublic@interfaceTimed{

@NonBindingStringname()default"";

@NonBindingbooleanabsolute()defaultfalse;}

Howtoobtaintherequired@Timed ?

Wecannottouchthecomponentsource/binary!

Page 48: Going further with CDI

Rememberthe AnnotatedType SPI?

ThankstoDeltaSpikewecaneasilycreatetherequired AnnotatedType

AnnotatedTypecreateTimedAnnotatedType()throwsException{AnnotationnonBinding=newAnnotationLiteral<Nonbinding>(){};returnnewAnnotatedTypeBuilder().readFromType(Timed.class).addToMethod(Timed.class.getMethod("name"),nonBinding).addToMethod(Timed.class.getMethod("absolute"),nonBinding).create();}

Thiscreatesaninstanceof @NonBinding annotation

Itwouldhavebeenpossiblebutfarmoreverbosetocreatethis AnnotatedType withoutthehelpofDeltaSpike.The AnnotatedTypeBuilder isinitializedfromtheMetrics @Timed annotation.

@NonBinding isaddedtobothmembersofthe @Timed annotation

123

3

1

2

3

Page 49: Going further with CDI

Add @Timed tothelistofinterceptorbindingwithanextension

Byobserving BeforeBeanDiscovery lifecycleevent

publicinterfaceBeforeBeanDiscovery{

addQualifier(Class<?extendsAnnotation>qualifier);addQualifier(AnnotatedType<?extendsAnnotation>qualifier);addScope(Class<?extendsAnnotation>scopeType,booleannormal,booleanpassivation);addStereotype(Class<?extendsAnnotation>stereotype,Annotation...stereotypeDef);addInterceptorBinding(AnnotatedType<?extendsAnnotation>bindingType);addInterceptorBinding(Class<?extendsAnnotation>bindingType,Annotation...bindingTypeDef);addAnnotatedType(AnnotatedType<?>type);addAnnotatedType(AnnotatedType<?>type,Stringid);}

Thismethodistheoneweneedtoaddourmodified @Timed AnnotatedType

1

1

Page 50: Going further with CDI

InternalStep HappenOnce LooponElements

BeforeBeanDiscovery isfirstinlifecycle

DeploymentStart

BeforeBean

Discovery

ScanArchive

ProcessAnnotated

Type

AfterType

Discovery

BeanEligibility

Check

ProcessInjectionPoint

ProcessInjectionTarget

ProcessBean

Attributes

ProcessBean

ProcessProducer

ProcessObserverMethod

AfterBean

Discovery

AfterDeploymentValidation

ApplicationRunning

BeforeShutdown

UndeployApplication

Page 51: Going further with CDI

ThisextensionwilldothejobclassMetricsExtensionimplementsExtension{

voidaddTimedBinding(@ObservesBeforeBeanDiscoverybbd)throwsException{bbd.addInterceptorBinding(createTimedAnnotatedType());}

privateAnnotatedTypecreateTimedAnnotatedType()throwsException{AnnotationnonBinding=newAnnotationLiteral<Nonbinding>(){};returnnewAnnotatedTypeBuilder().readFromType(Timed.class).addToMethod(Timed.class.getMethod("name"),nonBinding).addToMethod(Timed.class.getMethod("absolute"),nonBinding).create();}}

Page 52: Going further with CDI

Firstgoalachieved

Wecannowwrite:

@Timed("timer")voidtimedMethod(){//Businesscode}

AndhaveaMetricsTimer appliedtothemethod

Page 53: Going further with CDI

Secondgoal:Automaticallyregistercustommetrics

Whywouldwewantcustommetrics?

@AroundInvokeObjecttimedMethod(InvocationContextcontext)throwsException{Stringname=context.getMethod().getAnnotation(Timed.class).name();Timertimer=registry.timer(name);Timer.Contexttime=timer.time();try{returncontext.proceed();}finally{time.stop();}}

Theregistryprovidesadefault Timer (ifnonewasregisteredbytheuser).Thedefaulttimerhistogramisexponentiallybiasedtothepast5minutesofmeasurements.Wemaywanttohaveanotherbehavior.

1

1

Page 54: Going further with CDI

Automaticallyregistercustommetrics

Wewanttowritethis:

@Produces@Metric(name="myTimer")Timertimer=newTimer(newSlidingTimeWindowReservoir(1L,TimeUnit.MINUTES));

Andhave:

1. Thepossibilitytoretrievethis Timer fromtheregistrywhenit’sinjected(insteadofhavinganewinstancecreated)

2. This Timer producedwhenneeded(firstuse)3. This Timer registeredintheregistrywithitsname(here "myTimer" )

Thereare2 Metric :the com.codahale.metrics.Metric interfaceandthe com.codahale.metrics.annotation.Metric annotation

Page 55: Going further with CDI

Howtoachievethis?

Weneedtowriteanextensionthatwill:

1. Declare @Metric asaqualifiertoeaseinjectionandnameresolutionina BeforeBeanDiscovery observer

2. Changehowa Metric instancewillbeproducedtolookitupintheregistryandproduce(andregister)itonlyifit’snotfound.We’lldothisby:1. observingthe ProcessProducer lifecycleevent2. decoratingMetric Producer toaddthisnewbehavior

3. Produceall Metric instancesattheendofboottimetohavetheminregistryforruntime1. we’lldothisbyobserving AfterDeploymentValidation event

Page 56: Going further with CDI

InternalStep HappenOnce LooponElements

Sowewill @Observes these3eventstoaddourfeatures

DeploymentStart

BeforeBean

Discovery

ScanArchive

ProcessAnnotated

Type

AfterType

Discovery

BeanEligibility

Check

ProcessInjectionPoint

ProcessInjectionTarget

ProcessBean

Attributes

ProcessBean

ProcessProducer

ProcessObserverMethod

AfterBean

Discovery

AfterDeploymentValidation

ApplicationRunning

BeforeShutdown

UndeployApplication

Page 57: Going further with CDI

Adding @Metric tothelistofqualifiers

Thistimeweneedannotationmemberstobe"binding"( @Metric("a") and @Metric("b") shouldbedistinguished)

Sowedon’thavetoadd @Nonbinding annotationtothem

publicclassMetricExtensionimplementsExtension{

voidaddMetricQualifier(@ObservesBeforeBeanDiscoverybbd){bbd.addQualifier(Metric.class);}//...}

Page 58: Going further with CDI

Customizing Metric producingprocess

Wefirstneedtocreateourimplementationofthe Producer<X> SPI

classMetricProducer<XextendsMetric>implementsProducer<X>{

privatefinalProducer<X>delegate;

privatefinalBeanManagerbm;

privatefinalStringname;

MetricProducer(Producer<X>delegate,BeanManagerbm,Stringname){this.decorated=decorated;this.bm=bm;this.name=name;}//...

Page 59: Going further with CDI

Customizing Metric producingprocess(continued)//...@OverridepublicXproduce(CreationalContext<X>ctx){MetricRegistryregistry=BeanProvider.getContextualReference(bm,MetricRegistry.class,false);if(!registry.getMetrics().containsKey(name))registry.register(name,delegate.produce(ctx));return(X)registry.getMetrics().get(name);}

@Overridepublicvoiddispose(Xinstance){}

@OverridepublicSet<InjectionPoint>getInjectionPoints(){returndecorated.getInjectionPoints();}}

BeanProvider isaDeltaSpikehelperclasstoeasilyretrieveabeanorbeaninstance

Wedon’twanttohavetheproduced Metric instancedestroyedbytheCDIcontainer

1

2

1

2

Page 60: Going further with CDI

We’lluseour Producer<Metric> ina ProcessProducer observer

Throughthiseventwecansubstitutethestandardproducerbyours

publicinterfaceProcessProducer<T,X>{

AnnotatedMember<T>getAnnotatedMember();

Producer<X>getProducer();

voidsetProducer(Producer<X>producer);

voidaddDefinitionError(Throwablet);}

Getsthe AnnotatedMember associatedtothe @Produces fieldormethod

Getsthedefaultproducer(usefultodecorateit)

Overridestheproducer

1

2

3

1

2

3

Page 61: Going further with CDI

Customizing Metric producingprocess(end)

Here’stheextensioncodetodothisproducerdecoration

publicclassMetricExtensionimplementsExtension{//...<Xextendscom.codahale.metrics.Metric>voiddecorateMetricProducer(@ObservesProcessProducer<?,X>pp,BeanManagerbm){Stringname=pp.getAnnotatedMember().getAnnotation(Metric.class).name();pp.setProducer(newMetricProducer<>(pp.getProducer(),bm,name));}//...}

Page 62: Going further with CDI

Producingallthe Metric instancesattheendofboottime

Wedothatbyobservingthe AfterDeploymentValidation event

publicclassMetricExtensionimplementsExtension{//...voidregisterProduceMetrics(@ObservesAfterDeploymentValidationadv,BeanManagerbm){for(Bean<?>bean:bm.getBeans(com.codahale.metrics.Metric.class,newAnyLiteral()))for(Annotationqualifier:bean.getQualifiers())if(qualifierinstanceofMetric)BeanProvider.getContextualReference(bm,com.codahale.metrics.Metric.class,false,qualifier);}//...}

Getsallthe Metric beans

Retrievesaninstancethatwilluseourcustomproducerandthuswillfilltheregistry

1

2

1

2

Page 63: Going further with CDI

Secondgoalachieved

Wecannowwrite:

@Produces@Metric(name="myTimer")Timertimer=newTimer(newSlidingTimeWindowReservoir(1L,TimeUnit.MINUTES));

@InjectMetricRegistryregistry;

@Inject@Metric("myTimer")Metrictimer;

Andbesurethat registry.getMetrics().get("myTimer") andtimer arethesameobject(ourcustom Timer )

Page 64: Going further with CDI

CompleteextensioncodepublicclassMetricExtensionimplementsExtension{

voidaddMetricQualifier(@ObservesBeforeBeanDiscoverybbd){bbd.addQualifier(Metric.class);}

voidaddTimedInterceptorBinding(@ObservesBeforeBeanDiscoverybbd)throwsNoSuchMethodException{AnnotationnonBinding=newAnnotationLiteral<Nonbinding>(){};bbd.addInterceptorBinding(newAnnotatedTypeBuilder().readFromType(Timed.class).addToMethod(Timed.class.getMethod("name"),nonBinding).addToMethod(Timed.class.getMethod("absolute"),nonBinding).create());}

<Textendscom.codahale.metrics.Metric>voiddecorateMetricProducer(@ObservesProcessProducer<?,T>pp,BeanManagerbm){Stringname=pp.getAnnotatedMember().getAnnotation(Metric.class).name();pp.setProducer(newMetricProducer<>(pp.getProducer(),bm,name);}

voidregisterProduceMetrics(@ObservesAfterDeploymentValidationadv,BeanManagerbm){for(Bean<?>bean:bm.getBeans(com.codahale.metrics.Metric.class,newAnyLiteral()))for(Annotationqualifier:bean.getQualifiers())if(qualifierinstanceofMetric)BeanProvider.getContextualReference(bm,com.codahale.metrics.Metric.class,false,qualifier);}}

Page 65: Going further with CDI

TestyourCDIknowledge

Quizztime

Page 66: Going further with CDI

FindthevalidinjectionspointsclassMySuperBean{

@InjectBean<MySuperBean>myMeta;//A[]

@InjectBean<MyService>serviceMeta;//B[]

publicMySuperBean(@InjectMyServiceservice){/*...*/}//C[]

@InjectprivatevoidmyInitMethod(MyServiceservice){/*...*/}//D[]

@Inject@PostConstructpublicvoidmyInitMethod2(MyServiceservice){/*...*/}//E[]}

Page 67: Going further with CDI

SolutionclassMySuperBean{

@InjectBean<MySuperBean>myMeta;//A[X]

@InjectBean<MyService>serviceMeta;//B[]

publicMySuperBean(@InjectMyServiceservice){/*...*/}//C[]

@InjectprivatevoidmyInitMethod(MyServiceservice){/*...*/}//D[X]

@Inject@PostConstructpublicvoidmyInitMethod2(MyServiceservice){/*...*/}//E[]}

Page 68: Going further with CDI

FindBeanscandidateswithout beans.xml injar(CDI1.2)

@DecoratorpublicabstractclassMyDecoratorimplementsMyService{/*...*/}//A[]

@StatelesspublicclassMyServiceImplimplementsMyService{/*...*/}//B[]

publicclassMyBean{/*...*/}//C[]

@ModelpublicclassMyBean{/*...*/}//D[]

@SingletonpublicclassMyBean{/*...*/}//E[]

@ConversationScopedpublicclassMyBean{/*...*/}//F[]

Page 69: Going further with CDI

Solution@DecoratorpublicabstractclassMyDecoratorimplementsMyService{/*...*/}//A[X]

@StatelesspublicclassMyServiceImplimplementsMyService{/*...*/}//B[X]

publicclassMyBean{/*...*/}//C[]

@ModelpublicclassMyBean{/*...*/}//D[X]

@SingletonpublicclassMyBean{/*...*/}//E[]

@ConversationScopedpublicclassMyBean{/*...*/}//F[X]

Page 70: Going further with CDI

Findthevalidproducers@ApplicationScopedpublicclassMyBean{

@ProducespublicServiceproduce1(InjectionPointip,Bean<Service>myMeta){/*...*/}//A[]

@Produces@SessionScopedpublicServiceproduce2(InjectionPointip){/*...*/}//B[]

@ProducespublicMap<K,V>produceMap(InjectionPointip){/*...*/}//C[]

@ProducespublicMap<String,?extendsService>produceMap2(){/*...*/}//D[]}

Page 71: Going further with CDI

Solution@ApplicationScopedpublicclassMyBean{

@ProducespublicServiceproduce1(InjectionPointip,Bean<Service>myMeta){/*...*/}//A[X]

@Produces@SessionScopedpublicServiceproduce2(InjectionPointip){/*...*/}//B[]

@ProducespublicMap<K,V>produceMap(InjectionPointip){/*...*/}//C[X]

@ProducespublicMap<String,?extendsService>produceMap2(){/*...*/}//D[]}

Page 72: Going further with CDI

Whichobserverswillbetriggered?classFirstBean{

@InjectEvent<Post>postEvent;

publicvoidsaveNewPost(PostmyPost){postEvent.select(newAnnotationLiteral()<French>{}).fire(myPost);}}

classSecondBean{

voidlistenFrPost(@Observes@FrenchPostpost){/*...*/}//A[]voidlistenPost(@ObservesPostpost){/*...*/}//B[]voidlistenEnPost(@Observes@EnglishPostpost){/*...*/}//C[]voidlistenObject(@ObservesObjectobj){/*...*/}//D[]}

Page 73: Going further with CDI

SolutionclassFirstBean{

@InjectEvent<Post>postEvent;

publicvoidsaveNewPost(PostmyPost){postEvent.select(newAnnotationLiteral()<French>{}).fire(myPost);}}

classSecondBean{

voidlistenFrPost(@Observes@FrenchPostpost){/*...*/}//A[X]voidlistenPost(@ObservesPostpost){/*...*/}//B[X]voidlistenEnPost(@Observes@EnglishPostpost){/*...*/}//C[]voidlistenObject(@ObservesObjectobj){/*...*/}//D[X]}

Page 74: Going further with CDI

HowtouseCDIasdependencyinjectioncontainerforanintegrationframework(ApacheCamel)

CamelCDI

Page 75: Going further with CDI

AboutApacheCamel

Open-sourceintegrationframeworkbasedonknownEnterpriseIntegrationPatterns

ProvidesavarietyofDSLstowriteroutingandmediationrules

ProvidessupportforbeanbindingandseamlessintegrationwithDIframeworks

Page 76: Going further with CDI

DiscoverhowwecreatedCDIintegrationmoduleforCamel

Page 77: Going further with CDI

Cameloutofthebox(withoutCDI)publicstaticvoidmain(String[]args){CamelContextcontext=newDefaultCamelContext();context.addRoutes(newRouteBuilder(){publicvoidconfigure(){from("file:target/input?delay=1000").convertBodyTo(String.class).log("Sendingmessage[${body}]toJMS...").to("sjms:queue:output");}});PropertiesComponentproperties=newPropertiesComponent();properties.setLocation("classpath:camel.properties");context.addComponent("properties",properties);//Registersthe"properties"component

SjmsComponentcomponent=newSjmsComponent();component.setConnectionFactory(newActiveMQConnectionFactory("vm://broker?broker.persistent=false"));jms.setConnectionCount(Integer.valueOf(context.resolvePropertyPlaceholders("{{jms.maxConnections}}")));context.addComponent("sjms",jms);//Registersthe"sjms"component

context.start();}

ThisroutewatchesadirectoryeverysecondandsendsnewfilescontenttoaJMSqueue

1

1

Page 78: Going further with CDI

BasicCDIintegration(1/3)1. CamelcomponentsandroutebuilderasCDIbeans2. BindtheCamelcontextlifecycletothatoftheCDIcontainer

classFileToJmsRouteBeanextendsRouteBuilder{

@Overridepublicvoidconfigure(){from("file:target/input?delay=1000").convertBodyTo(String.class).log("Sendingmessage[${body}]toJMS...").to("sjms:queue:output");}}

Page 79: Going further with CDI

BasicCDIintegration(2/3)classPropertiesComponentFactoryBean{

@Produces@ApplicationScopedPropertiesComponentpropertiesComponent(){PropertiesComponentproperties=newPropertiesComponent();properties.setLocation("classpath:camel.properties");returnproperties;}}

classJmsComponentFactoryBean{

@Produces@ApplicationScopedSjmsComponentsjmsComponent(PropertiesComponentproperties)throwsException{SjmsComponentjms=newSjmsComponent();jms.setConnectionFactory(newActiveMQConnectionFactory("vm://broker?broker.persistent=false"));jms.setConnectionCount(Integer.valueOf(properties.parseUri("{{jms.maxConnections}}")));returncomponent;}}

Page 80: Going further with CDI

BasicCDIintegration(3/3)@ApplicationScopedclassCamelContextBeanextendsDefaultCamelContext{

@InjectCamelContextBean(FileToJmsRouteBeanroute,SjmsComponentjms,PropertiesComponentproperties){addComponent("properties",properties);addComponent("sjms",jms);addRoutes(route);}@PostConstructvoidstartContext(){super.start();}@PreDestroyvoidpreDestroy(){super.stop();}}

WecouldhavealotmorewithadvancedCDIfeatures

Page 81: Going further with CDI

Ourgoals1. Avoidassemblingandconfiguringthe CamelContext manually2. AccessCDIbeansfromtheCamelDSLautomatically

.to("sjms:queue:output");//Lookupbyname(sjms)andtype(Component)

context.resolvePropertyPlaceholders("{{jms.maxConnections}}");//Lookupbyname(properties)andtype(Component)

3. SupportCamelannotationsinCDIbeans@PropertyInject(value="jms.maxConnections",defaultValue="10")intmaxConnections;

Page 82: Going further with CDI

StepstointegrateCamelandCDI

Managethecreationandtheconfigurationofthe CamelContextbean

Bindthe CamelContext lifecyclethatoftheCDIcontainer

ImplementtheCamelSPItolookupCDIbeanreferences

Useacustom InjectionTarget forCDIbeanscontainingCamelannotations

Usethemagic

Page 83: Going further with CDI

Howtoachievethis?

Weneedtowriteanextensionthatwill:

1. Declarea CamelContext beanbyobservingthe AfterBeanDiscoverylifecycleevent

2. Instantiatethebeansoftype RouteBuilder andaddthemtotheCamelcontext

3. Start(resp.stop)theCamelcontextwhentheAfterDeploymentValidation eventisfired(resp.the BeforeShutdownevent)

4. CustomizetheCamelcontexttoquerythe BeanManager tolookupCDIbeansbynameandtype

5. DetectCDIbeanscontainingCamelannotationsbyobservingtheProcessAnnotatedType eventandmodifyhowtheygetinjectedbyobservingthe ProcessInjectionTarget lifecycleevent

Page 84: Going further with CDI

InternalStep HappenOnce LooponElements

Sowewill @Observes these5eventstoaddourfeatures

DeploymentStart

BeforeBean

Discovery

ScanArchive

ProcessAnnotated

Type

AfterType

Discovery

BeanEligibility

Check

ProcessInjectionPoint

ProcessInjectionTarget

ProcessBean

Attributes

ProcessBean

ProcessProducer

ProcessObserverMethod

AfterBean

Discovery

AfterDeploymentValidation

ApplicationRunning

BeforeShutdown

UndeployApplication

Page 85: Going further with CDI

Addingthe CamelContext bean

Automaticallyadda CamelContext beaninthedeploymentarchive

Howtoaddabeanprogrammatically?

Page 86: Going further with CDI

Declaringabeanprogrammatically

Weneedtoimplementthe Bean SPI

publicinterfaceBean<T>extendsContextual<T>,BeanAttributes<T>{

Class<?>getBeanClass();Set<InjectionPoint>getInjectionPoints();Tcreate(CreationalContext<T>creationalContext);//Contextual<T>voiddestroy(Tinstance,CreationalContext<T>creationalContext);Set<Type>getTypes();//BeanAttributes<T>Set<Annotation>getQualifiers();Class<?extendsAnnotation>getScope();StringgetName();Set<Class<?extendsAnnotation>>getStereotypes();booleanisAlternative();}

Page 87: Going further with CDI

Implementingthe Bean SPIclassCamelContextBeanimplementsBean<CamelContext>{

publicClass<?extendsAnnotation>getScope(){returnApplicationScoped.class;}

publicSet<Annotation>getQualifiers(){returnCollections.singleton((Annotation)newAnnotationLiteral<Default>(){});}publicSet<Type>getTypes(){returnCollections.singleton((Type)CamelContext.class);}

publicCamelContextcreate(CreationalContext<CamelContext>creational){returnnewDefaultCamelContext();}publicvoiddestroy(CamelContextinstance,CreationalContext<CamelContext>creational){}

publicClass<?>getBeanClass(){returnDefaultCamelContext.class;}

publicSet<InjectionPoint>getInjectionPoints(){returnCollections.emptySet();}

publicSet<Class<?extendsAnnotation>>getStereotypes(){returnCollections.emptySet();}publicStringgetName(){return"camel-cdi";}publicbooleanisAlternative(){returnfalse;}publicbooleanisNullable(){returnfalse;}}

Page 88: Going further with CDI

Addingaprogrammaticbeantothedeployment

Thenaddthe CamelContextBean beanprogrammaticallybyobservingthe AfterBeanDiscovery lifecyleevent

publicclassCamelExtensionimplementsExtension{

voidaddCamelContextBean(@ObservesAfterBeanDiscoveryabd){abd.addBean(newCamelContextBean());}}

Page 89: Going further with CDI

InstantiateandassembletheCamelcontext

Instantiatethe CamelContext beanandthe RouteBuilder beansintheAfterDeploymentValidation lifecycleevent

publicclassCamelExtensionimplementsExtension{//...voidconfigureContext(@ObservesAfterDeploymentValidationadv,BeanManagerbm){CamelContextcontext=getReference(bm,CamelContext.class);for(Bean<?>bean:bm.getBeans(RoutesBuilder.class))context.addRoutes(getReference(bm,RouteBuilder.class,bean));}<T>TgetReference(BeanManagerbm,Class<T>type){returngetReference(bm,type,bm.resolve(bm.getBeans(type)));}<T>TgetReference(BeanManagerbm,Class<T>type,Bean<?>bean){return(T)bm.getReference(bean,type,bm.createCreationalContext(bean));}}

Page 90: Going further with CDI

ManagedtheCamelcontextlifecycle

Start(resp.stop)theCamelcontextwhentheAfterDeploymentValidation eventisfired(resp.the BeforeShutdown )

publicclassCamelExtensionimplementsExtension{//...voidconfigureContext(@ObservesAfterDeploymentValidationadv,BeanManagerbm){CamelContextcontext=getReference(bm,CamelContext.class);for(Bean<?>bean:bm.getBeans(RoutesBuilder.class)context.addRoutes(getReference(bm,RouteBuilder.class,bean);context.start();}voidstopCamelContext(@ObservesBeforeShutdownbs,BeanManagerbm){CamelContextcontext=getReference(bm,CamelContext.class);context.stop();}}

Page 91: Going further with CDI

Firstgoalachieved

Wecangetridofthefollowingcode:

@ApplicationScopedclassCamelContextBeanextendsDefaultCamelContext{@InjectCamelContextBean(FileToJmsRouteBeanroute,SjmsComponentjms,PropertiesComponentproperties){addComponent("properties",propertiesComponent);addComponent("sjms",sjmsComponent);addRoutes(route);}@PostConstructvoidstartContext(){super.start();}@PreDestroyvoidstopContext(){super.stop();}}

Page 92: Going further with CDI

Secondgoal:AccessCDIbeansfromtheCamelDSL

HowtoretrieveCDIbeansfromtheCamelDSL?

.to("sjms:queue:output");//Lookupbyname(sjms)andtype(Component)context.resolvePropertyPlaceholders("{{jms.maxConnections}}");//Lookupbyname(properties)andtype(Component)

//Andalso....bean(MyBean.class);//LookupbytypeandDefaultqualifier.beanRef("beanName");//Lookupbyname

ImplementtheCamelregistrySPIandusethe BeanManager tolookupforCDIbeancontextualreferencesbynameandtype

Page 93: Going further with CDI

ImplementtheCamelregistrySPIclassCamelCdiRegistryimplementsRegistry{privatefinalBeanManagerbm;

CamelCdiRegistry(BeanManagerbm){this.bm=bm;}

public<T>TlookupByNameAndType(Stringname,Class<T>type){returngetReference(bm,type,bm.resolve(bm.getBeans(name)));}public<T>Set<T>findByType(Class<T>type){returngetReference(bm,type,bm.resolve(bm.getBeans(type)));}publicObjectlookupByName(Stringname){returnlookupByNameAndType(name,Object.class);}<T>TgetReference(BeanManagerbm,Typetype,Bean<?>bean){return(T)bm.getReference(bean,type,bm.createCreationalContext(bean));}}

Page 94: Going further with CDI

Addthe CamelCdiRegistry totheCamelcontext

classCamelContextBeanimplementsBean<CamelContext>{privatefinalBeanManagerbm;

CamelContextBean(BeanManagerbm){this.bm=bm;}//...publicCamelContextcreate(CreationalContext<CamelContext>creational){returnnewDefaultCamelContext(newCamelCdiRegistry(bm));}}

publicclassCamelExtensionimplementsExtension{//...voidaddCamelContextBean(@ObservesAfterBeanDiscoveryabd,BeanManagerbm){abd.addBean(newCamelContextBean(bm));}}

Page 95: Going further with CDI

Secondgoalachieved1/3

Wecandeclarethe sjms componentwiththe @Named qualifier

classJmsComponentFactoryBean{

@Produces@Named("sjms")@ApplicationScopedSjmsComponentsjmsComponent(PropertiesComponentproperties){SjmsComponentjms=newSjmsComponent();jms.setConnectionFactory(newActiveMQConnectionFactory("vm://broker?..."));jms.setConnectionCount(Integer.valueOf(properties.parseUri("{{jms.maxConnections}}")));returncomponent;}}

Page 96: Going further with CDI

Secondgoalachieved2/3

Declarethe properties componentwiththe @Named qualifier

classPropertiesComponentFactoryBean{

@Produces@Named("properties")@ApplicationScopedPropertiesComponentpropertiesComponent(){PropertiesComponentproperties=newPropertiesComponent();properties.setLocation("classpath:camel.properties");returnproperties;}}

Page 97: Going further with CDI

Secondgoalachieved3/3

Andgetridofthecoderelatedtothe properties and sjmscomponentsregistration

@ApplicationScopedclassCamelContextBeanextendsDefaultCamelContext{@InjectCamelContextBean(FileToJmsRouteBeanroute,SjmsComponentjms,PropertiesComponentproperties){addComponent("properties",propertiesComponent);addComponent("sjms",sjmsComponent);addRoutes(route);}@PostConstructvoidstartContext(){super.start();}@PreDestroyvoidstopContext(){super.stop();}}

Page 98: Going further with CDI

Thirdgoal:SupportCamelannotationsinCDIbeans

CamelprovidesasetofDIframeworkagnosticannotationsforresourceinjection

@PropertyInject(value="jms.maxConnections",defaultValue="10")intmaxConnections;

//Butalso...@EndpointInject(uri="jms:queue:foo")Endpointendpoint;

@BeanInject("foo")FooBeanfoo;

Howtosupportcustomannotationsinjection?

Page 99: Going further with CDI

Howtosupportcustomannotationsinjection?

Createacustom InjectionTarget thatusesthedefaultCamelbeanpostprocessor DefaultCamelBeanPostProcessor

publicinterfaceInjectionTarget<T>extendsProducer<T>{voidinject(Tinstance,CreationalContext<T>ctx);voidpostConstruct(Tinstance);voidpreDestroy(Tinstance);}

HookitintotheCDIinjectionmechanismbyobservingtheProcessInjectionTarget lifecycleevent

OnlyforbeanscontainingCamelannotationsbyobservingtheProcessAnnotatedType lifecycleandusing @WithAnnotations

Page 100: Going further with CDI

Createacustom InjectionTargetclassCamelInjectionTarget<T>implementsInjectionTarget<T>{

finalInjectionTarget<T>delegate;

finalDefaultCamelBeanPostProcessorprocessor;

CamelInjectionTarget(InjectionTarget<T>target,finalBeanManagerbm){delegate=target;processor=newDefaultCamelBeanPostProcessor(){publicCamelContextgetOrLookupCamelContext(){returngetReference(bm,CamelContext.class);}};}publicvoidinject(Tinstance,CreationalContext<T>ctx){processor.postProcessBeforeInitialization(instance,null);delegate.inject(instance,ctx);}//...}

CalltheCameldefaultbeanpost-processorbeforeCDIinjection

1

1

Page 101: Going further with CDI

Registerthecustom InjectionTarget

Observethe ProcessInjectionTarget lifecyleeventandsettheInjectionTarget

publicinterfaceProcessInjectionTarget<X>{AnnotatedType<X>getAnnotatedType();InjectionTarget<X>getInjectionTarget();voidsetInjectionTarget(InjectionTarget<X>injectionTarget);voidaddDefinitionError(Throwablet);}

Todecorateitwiththe CamelInjectionTarget

classCamelExtensionimplementsExtension{

<T>voidcamelBeansPostProcessor(@ObservesProcessInjectionTarget<T>pit,BeanManagerbm){pit.setInjectionTarget(newCamelInjectionTarget<>(pit.getInjectionTarget(),bm));}}

Page 102: Going further with CDI

ButonlyforbeanscontainingCamelannotationsclassCamelExtensionimplementsExtension{finalSet<AnnotatedType<?>>camelBeans=newHashSet<>();

voidcamelAnnotatedTypes(@Observes@WithAnnotations(PropertyInject.class)ProcessAnnotatedType<?>pat){camelBeans.add(pat.getAnnotatedType());}

<T>voidcamelBeansPostProcessor(@ObservesProcessInjectionTarget<T>pit,BeanManagerbm){if(camelBeans.contains(pit.getAnnotatedType()))pit.setInjectionTarget(newCamelInjectionTarget<>(pit.getInjectionTarget(),bm));}}

DetectallthetypescontainingCamelannotationswith @WithAnnotations

Decoratethe InjectionTarget correspondingtothesetypes

1

2

1

2

Page 103: Going further with CDI

Thirdgoalachieved1/2

Insteadofinjectingthe PropertiesComponent beantoresolveaconfigurationproperty

classJmsComponentFactoryBean{

@Produces@Named("sjms")@ApplicationScopedSjmsComponentsjmsComponent(PropertiesComponentproperties){SjmsComponentjms=newSjmsComponent();jms.setConnectionFactory(newActiveMQConnectionFactory("vm://broker?..."));jms.setConnectionCount(Integer.valueOf(properties.parseUri("{{jms.maxConnections}}")));returncomponent;}}

Page 104: Going further with CDI

Thirdgoalachieved2/2

Wecandirectlyrelyonthe @PropertyInject CamelannotationinCDIbeans

classJmsComponentFactoryBean{

@PropertyInject(value="jms.maxConnections",defaultValue="10")intmaxConnections;

@Produces@Named("sjms")@ApplicationScopedSjmsComponentsjmsComponent(){SjmsComponentcomponent=newSjmsComponent();jms.setConnectionFactory(newActiveMQConnectionFactory("vm://broker?..."));component.setConnectionCount(maxConnections);returncomponent;}}

Page 105: Going further with CDI

Bonusgoal:CamelDSLAOP

AOPinstrumentationoftheCamelDSL

from("file:target/input?delay=1000").convertBodyTo(String.class).log("Sendingmessage[${body}]toJMS...").to("sjms:queue:output");

withCDIobservers

from("file:target/input?delay=1000").convertBodyTo(String.class).to("sjms:queue:output").id("joinpoint");}voidadvice(@Observes@NodeId("joinpoint")Exchangeexchange){logger.info("Sendingmessage[{}]toJMS...",exchange.getIn().getBody());}

Page 106: Going further with CDI

Howtoachievethis?

WecancreateaCDIqualifiertoholdtheCamelnodeidmetadata:

@Qualifier@Retention(RetentionPolicy.RUNTIME)public@interfaceNodeId{Stringvalue();}

andcreateanextensionthatwill:

1. DetecttheCDIbeanscontainingobservermethodswiththe @NodeIdqualifierbyobservingthe ProcessObserverMethod eventandcollecttheCamelprocessornodestobeinstrumented

2. CustomizetheCamelcontextbyprovidinganimplementationoftheCamel InterceptStrategy interfacethatwillfireaCDIeventeachtimean Exchange isprocessedbytheinstrumentednodes

Page 107: Going further with CDI

DetecttheCamelDSLAOPobservermethods

Observethe ProcessObserverMethod lifecyleevent

publicinterfaceProcessObserverMethod<T,X>{AnnotatedMethod<X>getAnnotatedMethod();ObserverMethod<T>getObserverMethod();voidaddDefinitionError(Throwablet);}

Andcollecttheobservermethodmetadata

classCamelExtensionimplementsExtension{finalSet<NodeId>joinPoints=newHashSet<>();

voidpointcuts(@ObservesProcessObserverMethod<Exchange,?>pom){for(Annotationqualifier:pom.getObserverMethod().getObservedQualifiers())if(qualifierinstanceofNodeId)joinPoints.add(NodeId.class.cast(qualifier));}}

Page 108: Going further with CDI

InstrumenttheCamelcontext

InterceptmatchingnodesandfireaCDIevent

voidconfigureCamelContext(@ObservesAfterDeploymentValidationadv,finalBeanManagermanager){context.addInterceptStrategy(newInterceptStrategy(){publicProcessorwrapProcessorInInterceptors(CamelContextcontext,ProcessorDefinition<?>definition,Processortarget,ProcessornextTarget)throwsException{if(definition.hasCustomIdAssigned()){for(finalNodenode:joinPoints){if(node.value().equals(definition.getId())){returnnewDelegateAsyncProcessor(target){publicbooleanprocess(Exchangeexchange,AsyncCallbackcallback){manager.fireEvent(exchange,node);returnsuper.process(exchange,callback);}};}}}returntarget;}});}

Page 109: Going further with CDI

Bonusgoalachieved

WecandefinejoinpointsintheCamelDSL

from("file:target/input?delay=1000").convertBodyTo(String.class).to("sjms:queue:output").id("joinpoint");}

AndadvisethemwithCDIobservers

voidadvice(@Observes@NodeId("joinpoint")Exchangeexchange){List<MessageHistory>history=exchange.getProperty(Exchange.MESSAGE_HISTORY,List.class);logger.info("Sendingmessage[{}]to[{}]...",exchange.getIn().getBody(),history.get(history.size()-1).getNode().getLabel());}

Page 110: Going further with CDI

Conclusion

Page 111: Going further with CDI

References

CDISpecification-cdi-spec.org

MetricsCDIsources-github.com/astefanutti/metrics-cdi

CamelCDIsources-github.com/astefanutti/camel-cdi

Slidessources-github.com/astefanutti/further-cdi

SlidesgeneratedwithAsciidoctor,PlantUMLandDZSlidesbackend

Originalslidetemplate-DanAllen&SarahWhite

Page 112: Going further with CDI

AntoineSabot-DurandAntoninStefanutti@antoine_sd@astefanut

Page 113: Going further with CDI

Annexes

Page 114: Going further with CDI

CompletelifecycleeventsApplication lifecycle

BeforeBeanDiscovery

AfterTypeDiscovery

AfterBeanDiscovery

AfterDeploymentValidation

Type Discovery

ProcessAnnotatedType<X>

yes

whiletypesindeploymentarchive?

no

Bean Discovery

For each discovered types during type discovery

ProcessInjectionPoint<T,X>

ProcessInjectionTarget<X>

ProcessBeanAttributes<T>

ProcessManagedBean<X>

For each producer methods / fields of enabled beans

ProcessInjectionPoint<T,X>

ProcessProducer<T,X>

ProcessBeanAttributes<T>

ProcessProducerMethod<T,X>ProcessProducerField<T,X>

For each observer methods of enabled beans

ProcessInjectionPoint<T,X>

ProcessObserverMethod<T,X>