112
Going further with CDI 1.2 Antoine Sabot-Durand · Antonin Stefanutti

Advanced CDI in live coding

Embed Size (px)

Citation preview

Page 1: Advanced CDI in live coding

GoingfurtherwithCDI1.2

AntoineSabot-Durand·AntoninStefanutti

Page 2: Advanced CDI in live coding

AntoninStefanutti

[email protected]/astefanutti

Page 3: Advanced CDI in live coding

AntoineSabot-Durand

RedHatCDIspeclead@antoine_sdnext-presso.comgithub.com/antoinesd

Page 4: Advanced CDI in live coding

ShouldIstayorshouldIgo?

AtalkaboutadvancedCDI

Mightbehardforbeginners

Don’tneedtobeaCDIguru

Page 5: Advanced CDI in live coding

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

ShouldIstayorshouldIgo?

Ifyouknowmostoftheseyoucanstay

Page 6: Advanced CDI in live coding

Moreconcretely

What’sincluded:

1. Realusecasesfromreallifewithrealusers2. Newapproachtointroduceportableextensionconcepts3. CodeinIDEwithtests

What’snotincluded:

1. IntroductiontoCDI2. Oldcontentonextension3. WorkwithContext(need2morehours)

Page 7: Advanced CDI in live coding

Toolsusedinthecode1/2

ApacheDeltaspike

1. ApacheDeltaSpikeisagreatCDItoolbox

2. Providehelperstodevelopextension3. Andacollectionofmoduleslike:

1. Security2. Data3. Scheduler

4. Moreinfoondeltaspike.apache.org

Page 8: Advanced CDI in live coding

Toolsusedinthecode2/2

Arquillian

1. Arquillianisanintegrationtestplatform2. ItintegrateswithJUnit3. Createyourdeploymentinadedicated

method4. Andlaunchyourtestsagainstthecontainer

ofyourchoice5. We’llusethe weld-se-embedded and

weld-ee-embedded container6. TherightsolutiontotestJavaEEcode7. Moreinfoonarquillian.org

Page 9: Advanced CDI in live coding

Agenda

Slidesavailableatastefanutti.github.io/further-cdi

MeetCDISPIIntroducingCDIExtensionsMetricsCDICamelCDI

Page 10: Advanced CDI in live coding

MeetCDISPI

Page 11: Advanced CDI in live coding

SPIcanbesplitin4parts

Page 12: Advanced CDI in live coding

SPIcanbesplitin4parts

Typemeta-model

Page 13: Advanced CDI in live coding

SPIcanbesplitin4parts

CDImeta-model

Page 14: Advanced CDI in live coding

SPIcanbesplitin4parts

CDIentrypoints

Page 15: Advanced CDI in live coding

SPIcanbesplitin4parts

SPIdedicatedtoextensions

Page 16: Advanced CDI in live coding

Whyhavingatypemeta-model?

Because @Annotations areconfiguration

buttheyarealsoread-only

Sotoconfigureweneedamutablemeta-model…

… forannotatedtypes

Page 17: Advanced CDI in live coding

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 18: Advanced CDI in live coding

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 19: Advanced CDI in live coding

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 20: Advanced CDI in live coding

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 21: Advanced CDI in live coding

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<?>)

somemethodsskipped

CDIT

Set<CDIProvider>discoveredProvidersCDIProviderconfiguredProvider

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

Page 22: Advanced CDI in live coding

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 23: Advanced CDI in live coding

AlltheseSPIinterfacesareeventscontainingmeta-modelSPI

TheseeventsfiredatboottimecanonlybeobservedinCDIextensions

Forinstance:

A ProcessAnnotatedType<T> eventisfiredforeachtypebeingdiscoveredatboottime

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

Page 24: Advanced CDI in live coding

IntroducingCDIPortableExtensions

Page 25: Advanced CDI in live coding

Portableextensions

OneofthemostpowerfulfeatureoftheCDIspecification

Notreallypopularized,partlydueto:

1. Theirhighlevelofabstraction2. ThegoodknowledgeonBasicCDIandSPI3. Lackofinformation(CDIisoftenreducedtoabasicDIsolution)

Page 26: Advanced CDI in live coding

Extensions,whatfor?

Tointegrate3rdpartylibraries,frameworksorlegacycomponents

Tochangeexistingconfigurationorbehavior

ToextendCDIandJavaEE

Thankstothem,JavaEEcanevolvebetweenmajorreleases

Page 27: Advanced CDI in live coding

Extensions,how?

ObservingSPIeventsatboottimerelatedtothebeanmanagerlifecycle

Checkingwhatmeta-dataarebeingcreated

Modifyingthesemeta-dataorcreatingnewones

Page 28: Advanced CDI in live coding

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 29: Advanced CDI in live coding

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 30: Advanced CDI in live coding

Example:IgnoringJPAentities

ThefollowingextensionpreventsCDItomanageentities

Thisisacommonlyadmittedgoodpractice

publicclassVetoEntityimplementsExtension{

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

Page 31: Advanced CDI in live coding

ExtensionsarelaunchedduringbootstrapandarebasedonCDIevents

Oncetheapplicationisbootstrapped,theBeanManagerisinread-onlymode(noruntimebeanregistration)

Youonlyhaveto@Observes built-inCDIeventstocreateyourextensions

Remember

Page 32: Advanced CDI in live coding

IntegratingDropwizardMetricsinCDI

MetricsCDI

Page 33: Advanced CDI in live coding

DropwizardMetricsprovides

Differentmetrictypes: Counter , Gauge , Meter , Timer ,…

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

MetricRegistry objectwhichcollectsallyourappmetrics

AnnotationsforAOPframeworks: @Counted , @Timed ,…

… butdoesnotincludeintegrationwiththeseframeworks

Moreatdropwizard.github.io/metrics

Page 34: Advanced CDI in live coding

DiscoverhowwecreatedCDIintegrationmoduleforMetrics

Page 35: Advanced CDI in live coding

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

classTimedMethodClass{

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

Notethatifa Timer named "timer" doesn’texist, MetricRegistry willcreateadefaultoneandregisterit

1

1

Page 36: Advanced CDI in live coding

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

classTimedMethodBean{@InjectMetricRegistryregistry;

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

WecouldhavealotmorewithadvancedCDIfeatures

Page 37: Advanced CDI in live coding

OurgoalstoachievefullCDIintegration

Produceandinjectmultiplemetricsofthesametype

ApplyMetricswithprovidedannotations

Accesssame Metric throughinjector MetricRegistry getter

Page 38: Advanced CDI in live coding

GOAL1Produceandinjectmultiplemetricsofthesametype

Page 39: Advanced CDI in live coding

What’stheproblemwithmultipleMetricsofthesametype?

Thiscodecreateadeploymenterror(ambiguousdependency)

@ProducesTimertimer=newTimer(newSlidingTimeWindowReservoir(1L,MINUTES));

@ProducesTimertimer=newTimer(newSlidingTimeWindowReservoir(1L,HOURS));

@InjectTimertimer;

Thistimerthatonlykeepsmeasurementoflastminuteisproducedasabeanoftype Timer

Thistimerthatonlykeepsmeasurementoflasthourisproducedasabeanoftype Timer

Thisinjectionpointisambiguoussince2eligiblebeansexist

1

2

3

1

2

3

Page 40: Advanced CDI in live coding

Solvingtheambiguity

Wecouldusetheprovided @Metric annotationtoqualifyourbeans

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

@Produces@Metric(name="mySecondTimer")Timertimer=newTimer(newSlidingTimeWindowReservoir(1L,HOURS));

@Inject@Metric(name="myTimer")Timertimer;

Thatwon’tworkoutoftheboxsince @Metric isnotaqualifier

Page 41: Advanced CDI in live coding

Howtomake @Metric aqualifier?

Byobserving BeforeBeanDiscovery lifecycleeventinanextension

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);}

Thismethodistheoneweneedtodeclare @Metric asqualifier

Anduse addQualifier() methodintheevent

1

1

Page 42: Advanced CDI in live coding

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 43: Advanced CDI in live coding

Ourfirstextension

ACDIextensionisaclassimplementingthe Extension taginterface

org.cdi.further.metrics.MetricsExtension

publicclassMetricsExtensionimplementsExtension{

voidaddMetricAsQualifier(@ObservesBeforeBeanDiscoverybdd){bdd.addQualifier(Metric.class);}}

Extensionisactivatedbyaddingthisfileto META-INF/services

javax.enterprise.inject.spi.Extension

org.cdi.further.metrics.MetricsExtension

Page 44: Advanced CDI in live coding

Goal1achieved Wecannowwrite:

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

@Produces@Metric(name="mySecondTimer")Timertimer=newTimer(newSlidingTimeWindowReservoir(1L,HOURS));

@Inject@Metric(name="myTimer")Timertimer;

Andhavethe Timer injectionpointssatisfied

Page 45: Advanced CDI in live coding

GOAL2ApplyMetricswithprovidedannotations

Page 46: Advanced CDI in live coding

Goal2indetail Wewanttobeabletowrite:

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

Andhavethetimer "timer" activatedduringmethodinvocation

Thesolutionistobuildandinterceptorandbinditto @Timed

1

Page 47: Advanced CDI in live coding

Goal2stepbystep

Createaninterceptorforthetimertechnicalcode

Make @Timed (providedbyMetrics)avalidinterceptorbinding

Programmaticallyadd @Timed asaninterceptorbinding

Usethemagic

Page 48: Advanced CDI in live coding

Preparinginterceptorcreation

Weshouldfindthetechnicalcodethatwillwrapthebusinesscode

classTimedMethodBean{

@InjectMetricRegistryregistry;

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

Page 49: Advanced CDI in live coding

Creatingtheinterceptor

Interceptorcodeishighlightedbelow

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

InCDIaninterceptorisabean,youcaninjectotherbeansinit

Herethebusinessoftheapplicationiscalled.Allthecodearoundisthetechnicalone.

1

2

1

2

Page 50: Advanced CDI in live coding

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

@InjectMetricRegistryregistry;

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

Givinga @Priority toaninterceptoractivatesandordersit

1

1

Page 51: Advanced CDI in live coding

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

@InjectMetricRegistryregistry;

@AroundInvokeObjecttimedMethod(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 52: Advanced CDI in live coding

Backoninterceptorbinding

Aninterceptorbindingisanannotationusedin2places:

1. Ontheinterceptorclasstobindittothisannotation2. Onthemethodsorclassestobeinterceptedbythisinterceptor

Aninterceptorbindingshouldhavethe @InterceptorBindingannotationorshouldbedeclaredprogrammatically

Iftheinterceptorbindingannotationhasmembers:

1. Theirvaluesaretakenintoaccounttoresolveinterceptor2. Unlessmembersareannotatedwith @NonBinding

Page 53: Advanced CDI in live coding

@Timed sourcecodeisnotaninterceptorbinding

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

public@interfaceTimed{

Stringname()default"";

booleanabsolute()defaultfalse;}

Lackof @InterceptorBinding annotation

Noneofthemembershavethe @NonBinding annotation,so @Timed(name="timer1") and@Timed(name="timer2") willbe2differentinterceptorbindings

1

2

2

1

2

Page 54: Advanced CDI in live coding

Therequired @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 55: Advanced CDI in live coding

Usingthe 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 56: Advanced CDI in live coding

Thisextensionwilldothejob

Weobserve BeforeBeanDiscovery toaddanewinterceptorbinding

publicclassMetricsExtensionimplementsExtension{

voidaddTimedBinding(@ObservesBeforeBeanDiscoverybdd)throwsException{AnnotationnonBinding=newAnnotationLiteral<Nonbinding>(){};

bdd.addInterceptorBinding(newAnnotatedTypeBuilder<Timed>().readFromType(Timed.class).addToMethod(Timed.class.getMethod("name"),nonBinding).addToMethod(Timed.class.getMethod("absolute"),nonBinding).create());}}

Page 57: Advanced CDI in live coding

Goal2achieved Wecannowwrite:

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

AndhaveaMetricsTimer appliedtothemethod

Interceptorshouldbeenhancedtosupport @Timed onaclass

Otherinterceptorshouldbedevelopedforothermetrics

Page 58: Advanced CDI in live coding

Ourgoals1. ApplyametricwiththeprovidedannotationinAOPstyle

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

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

AnnotationsprovidedbyMetrics

1

1

1

1

Page 59: Advanced CDI in live coding

GOAL3AccesssameMetricthrough@InjectorMetricRegistry getter

Page 60: Advanced CDI in live coding

Goal3indetail WhenWriting:

@Inject@Metric(name="myTimer")Timertimer1;

@InjectMetricRegistryregistry;

Timertimer2=registry.timer("myTimer");

… Wewantthat timer1==timer2

Page 61: Advanced CDI in live coding

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

@Inject@Metric(name="myTimer")Timertimer;

@InjectMetricRegistryregistry;

Timertimer=registry.timer("myTimer");

Produced Timer shouldbeaddedtoMetricRegistrywhenproduced

Whenretrievedfromregistrya Metric shouldbeidenticaltotheproducedinstanceandviceversa

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

1

2

1

2

Page 62: Advanced CDI in live coding

Goal3stepbystep

Weneedtowriteanextensionthatwill:

1. Changehowa Metric instanceisproducedbylookingitupintheregistryfirstandproducing(andregistering)itonlyifit’snotfound.We’lldothisby:1. observingthe ProcessProducer lifecycleevent2. decoratingMetric Producer toaddthisnewbehavior

2. Produceall Metric instancesattheendofboottimetohavetheminregistryforruntime1. we’lldothisbyobserving AfterDeploymentValidation event

Page 63: Advanced CDI in live coding

InternalStep HappenOnce LooponElements

Sowewill @Observes these2eventstoaddourfeatures

DeploymentStart

BeforeBean

Discovery

ScanArchive

ProcessAnnotated

Type

AfterType

Discovery

BeanEligibility

Check

ProcessInjectionPoint

ProcessInjectionTarget

ProcessBean

Attributes

ProcessBean

ProcessProducer

ProcessObserverMethod

AfterBean

Discovery

AfterDeploymentValidation

ApplicationRunning

BeforeShutdown

UndeployApplication

Page 64: Advanced CDI in live coding

Customizing Metric producingprocess

Wefirstneedtocreateourimplementationofthe Producer<X> SPI

classMetricProducer<XextendsMetric>implementsProducer<X>{

finalProducer<X>decorate;

finalStringmetricName;

MetricProducer(Producer<X>decorate,StringmetricName){this.decorate=decorate;this.metricName=metricName;}

//...

Page 65: Advanced CDI in live coding

Customizing Metric producingprocess(continued)//...publicXproduce(CreationalContext<X>ctx){MetricRegistryreg=BeanProvider.getContextualReference(MetricRegistry.class,false);if(!reg.getMetrics().containsKey(metricName))reg.register(metricName,decorate.produce(ctx));

return(X)reg.getMetrics().get(metricName);}

publicvoiddispose(Xinstance){}

publicSet<InjectionPoint>getInjectionPoints(){returndecorate.getInjectionPoints();}}

Producemethodwillbeusedbythecontaineratruntimetodecoratedeclaredproducerwithourlogic

BeanProvider isaDeltaSpikehelperclasstoeasilyretrieveabeanorbeaninstance

Ifmetricnameisnotintheregistry,theoriginalproduceriscalledanditsresultisaddedtoregistry

12

3

1

2

3

Page 66: Advanced CDI in live coding

We’lluseour MetricProducer ina ProcessProducer observer

Thiseventallowustosubstitutetheoriginalproducerbyours

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 67: Advanced CDI in live coding

Customizing Metric producingprocess(end)

Here’stheextensioncodetodothisproducerdecoration

publicclassMetricsExtensionimplementsExtension{//...<XextendsMetric>voiddecorateMetricProducer(@ObservesProcessProducer<?,X>pp){Stringname=pp.getAnnotatedMember().getAnnotation(Metric.class).name();pp.setProducer(newMetricProducer<>(pp.getProducer(),name));}//...}

Weretrievemetricnamefromthe name() memberin @Metric

Wereplacetheoriginalproducerbyourproducer(whichdecoratestheformer)

12

1

2

Page 68: Advanced CDI in live coding

Producingallthe Metric instancesattheendofboottime

Wedothatbyobservingthe AfterDeploymentValidation event

publicclassMetricsExtensionimplementsExtension{//...voidregisterProducedMetrics(@ObservesAfterDeploymentValidationadv){List<Metric>metrics=BeanProvider.getContextualReferences(Metric.class,true);}//...}

getContextualReferences() isamethodinDeltaSpike BeanProvider helperclass.Itcreatesthelistofbeaninstancesforagivenbeantype(ignoringqualifiers)

1

1

Page 69: Advanced CDI in live coding

Goal3achieved

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 70: Advanced CDI in live coding

CompleteextensioncodepublicclassMetricsExtensionimplementsExtension{

voidaddMetricAsQualifier(@ObservesBeforeBeanDiscoverybdd){bdd.addQualifier(Metric.class);}

voidaddTimedBinding(@ObservesBeforeBeanDiscoverybdd)throwsException{AnnotationnonBinding=newAnnotationLiteral<Nonbinding>(){};bdd.addInterceptorBinding(newAnnotatedTypeBuilder<Timed>().readFromType(Timed.class).addToMethod(Timed.class.getMethod("name"),nonBinding).addToMethod(Timed.class.getMethod("absolute"),nonBinding).create());}

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

voidregisterProducedMetrics(@ObservesAfterDeploymentValidationadv){List<com.codahale.metrics.Metric>metrics=BeanProvider.getContextualReferences(com.codahale.metrics.Metric.class,true);}}

Page 71: Advanced CDI in live coding

HowtouseCDIasdependencyinjectioncontainerforanintegrationframework(ApacheCamel)

CamelCDI

Page 72: Advanced CDI in live coding

AboutApacheCamel

Open-sourceintegrationframeworkbasedonknownEnterpriseIntegrationPatterns

ProvidesavarietyofDSLstowriteroutingandmediationrules

ProvidessupportforbeanbindingandseamlessintegrationwithDIframeworks

Page 73: Advanced CDI in live coding

DiscoverhowwecreatedCDIintegrationmoduleforCamel

Page 74: Advanced CDI in live coding

Cameloutofthebox(withoutCDI)publicstaticvoidmain(String[]args){CamelContextcontext=newDefaultCamelContext();context.addRoutes(newRouteBuilder(){publicvoidconfigure(){from("file:target/input?delay=1000").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 75: Advanced CDI in live coding

WhyCDI?

Page 76: Advanced CDI in live coding

BasicCDIintegration(1/3)1. CamelcomponentsandroutebuilderasCDIbeans2. BindtheCamelcontextlifecycletothatoftheCDIcontainer

classFileToJmsRouteBeanextendsRouteBuilder{

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

Page 77: Advanced CDI in live coding

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 78: Advanced CDI in live coding

BasicCDIintegration(3/3)@ApplicationScopedclassCamelContextBeanextendsDefaultCamelContext{

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

WecouldhavealotmorewithadvancedCDIfeatures

Page 79: Advanced CDI in live coding

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 80: Advanced CDI in live coding

StepstointegrateCamelandCDI

Managethecreationandtheconfigurationofthe CamelContextbean

Bindthe CamelContext lifecycletothatoftheCDIcontainer

ImplementtheCamelregistrySPItolookupCDIbeanreferences

Useacustom InjectionTarget forCDIbeanscontainingCamelannotations

Usethemagic

Page 81: Advanced CDI in live coding

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 82: Advanced CDI in live coding

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 83: Advanced CDI in live coding

Addingthe CamelContext bean

Automaticallyadda CamelContext beaninthedeploymentarchive

Howtoaddabeanprogrammatically?

Page 84: Advanced CDI in live coding

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 85: Advanced CDI in live coding

Implementingthe Bean SPIclassCamelContextBeanimplementsBean<CamelContext>{

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

publicSet<Annotation>getQualifiers(){returnCollections.<Annotation>singleton(newAnnotationLiteral<Default>(){});}publicSet<Type>getTypes(){returnCollections.<Type>singleton(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(){returnnull;}//Onlycalledfor@NamedbeanpublicbooleanisAlternative(){returnfalse;}publicbooleanisNullable(){returnfalse;}}

Page 86: Advanced CDI in live coding

Addingaprogrammaticbeantothedeployment

Thenaddthe CamelContextBean beanprogrammaticallybyobservingthe AfterBeanDiscovery lifecycleevent

publicclassCamelExtensionimplementsExtension{

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

Page 87: Advanced CDI in live coding

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 88: Advanced CDI in live coding

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 89: Advanced CDI in live coding

Firstgoalachieved

Wecangetridofthefollowingcode:

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

Page 90: Advanced CDI in live coding

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 91: Advanced CDI in live coding

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 92: Advanced CDI in live coding

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 93: Advanced CDI in live coding

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 94: Advanced CDI in live coding

Secondgoalachieved2/3

Declarethe properties componentwiththe @Named qualifier

classPropertiesComponentFactoryBean{

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

Page 95: Advanced CDI in live coding

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 96: Advanced CDI in live coding

Thirdgoal:SupportCamelannotationsinCDIbeans

CamelprovidesasetofDIframeworkagnosticannotationsforresourceinjection

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

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

@BeanInject("foo")FooBeanfoo;

Howtosupportcustomannotationsinjection?

Page 97: Advanced CDI in live coding

Howtosupportcustomannotationsinjection?

Createacustom InjectionTarget thatusesthedefaultCamelbeanpostprocessor DefaultCamelBeanPostProcessor

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

HookitintotheCDIinjectionmechanismbyobservingtheProcessInjectionTarget lifecycleevent

OnlyforbeanscontainingCamelannotationsbyobservingtheProcessAnnotatedType lifecycleandusing @WithAnnotations

Page 98: Advanced CDI in live coding

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 99: Advanced CDI in live coding

Registerthecustom InjectionTarget

Observethe ProcessInjectionTarget lifecycleeventandsettheInjectionTarget

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 100: Advanced CDI in live coding

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 101: Advanced CDI in live coding

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 102: Advanced CDI in live coding

Thirdgoalachieved2/2

Wecandirectlyrelyonthe @PropertyInject CamelannotationinCDIbeans

classJmsComponentFactoryBean{

@PropertyInject("jms.maxConnections")intmaxConnections;

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

Page 103: Advanced CDI in live coding

Bonusgoal:CamelDSLAOP

AOPinstrumentationoftheCamelDSL

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

WithCDIobservers

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

Page 104: Advanced CDI in live coding

Howtoachievethis?

WecancreateaCDIqualifiertoholdtheCamelnodeidmetadata:

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

Andcreateanextensionthatwill:

1. DetecttheCDIbeanscontainingobservermethodswiththe @NodeIdqualifierbyobservingthe ProcessObserverMethod eventandcollecttheCamelprocessornodestobeinstrumented

2. CustomizetheCamelcontextbyprovidinganimplementationoftheCamel InterceptStrategy interfacethatwillfireaCDIeventeachtimean Exchange isprocessedbytheinstrumentednodes

Page 105: Advanced CDI in live coding

DetecttheCamelDSLAOPobservermethods

Observethe ProcessObserverMethod lifecycleevent

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 106: Advanced CDI in live coding

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 107: Advanced CDI in live coding

Bonusgoalachieved

WecandefinejoinpointsintheCamelDSL

from("file:target/input?delay=1000").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(String.class),history.get(history.size()-1).getNode().getLabel());}

Page 108: Advanced CDI in live coding

Conclusion

Page 109: Advanced CDI in live coding

References

CDISpecification-cdi-spec.org

Slidessources-github.com/astefanutti/further-cdi

MetricsCDIsources-github.com/astefanutti/metrics-cdi

CamelCDIsources-github.com/astefanutti/camel-cdi

SlidesgeneratedwithAsciidoctor,PlantUMLandDZSlidesbackend

Originalslidetemplate-DanAllen&SarahWhite

Page 110: Advanced CDI in live coding

AntoineSabot-DurandAntoninStefanutti@antoine_sd@astefanut

Page 111: Advanced CDI in live coding

Annexes

Page 112: Advanced CDI in live coding

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>