going further with cdi

Post on 16-Jul-2015

106 Views

Category:

Technology

5 Downloads

Preview:

Click to see full reader

TRANSCRIPT

GoingfurtherwithCDI1.2

AntoineSabot-Durand·AntoninStefanutti

AntoninStefanutti

SoftwareEngineerMurexwww.murex.com@astefanutgithub.com/astefanutti

Slidesavailableatgithub.com/astefanutti/further-cdi

CDI@Murex

CDIastheproductivityecosystemtobuildconnectivityinterfaces

CDI@Murex

AntoineSabot-Durand

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

ShouldIstayorshouldIgo?

AtalkaboutadvancedCDI

Mightbehardforbeginners

Don’tneedtobeaCDIguru

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

ShouldIstayorshouldIgo?

Ifyouknowthemostoftheseyoucanstay

Moreconcretely

What’sincluded:

1. Realusecasesfromreallifewithrealusers2. Newapproachtointroduceportableextensionconcepts3. CodeinIDEwithtests

What’snotincluded:

1. IntroductiontoCDI2. Oldcontentonextension3. WorkwithContext(need2morehours)

Toolsusedinthecode1/2

ApacheDeltaspike

1. ApacheDeltaSpikeisagreatCDItoolbox

2. Providehelperstodevelopextension3. Andacollectionofmoduleslike:

1. Security2. Data3. Scheduler

4. Moreinfoondeltaspike.apache.org

Toolsusedinthecode2/2

Arquillian

1. Arquillianisanintegrationtestplatform2. ItintegrateswithJUnit3. Createyourdeploymentinadedicatedmethod

4. Andlaunchyourtestsagainstthecontainerofyourchoice

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

6. TherightsolutiontotestJavaEEcode7. Moreinfoonarquillian.org

Agenda

MeetCDISPIIntroducingCDIExtensionsMetricsCDICDIQuizzCamelCDI

Slidesavailableatastefanutti.github.io/further-cdi

MeetCDISPI

SPIcanbesplitin4parts

SPIcanbesplitin4parts

Typemeta-model

SPIcanbesplitin4parts

CDImeta-model

SPIcanbesplitin4parts

CDIentrypoints

SPIcanbesplitin4parts

SPIdedicatedtoextensions

Whyhavingatypemeta-model?

Because @Annotations areconfiguration

buttheyarealsoread-only

Sotoconfigureweneedamutablemeta-model…

… forannotatedtypes

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

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

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;

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

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

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

AlltheseSPIinterfacesareeventscontainingmeta-modelSPI

TheseeventsfiredatboottimecanonlybeobservedinCDIextensions

Forinstance:

A ProcessAnnotatedType<T> eventisfiredforeachtypebeingdiscoveredatboottime

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

IntroducingCDIPortableExtensions

Portableextensions

OneofthemostpowerfulfeatureoftheCDIspecification

Notreallypopularized,partlydueto:

1. Theirhighlevelofabstraction2. ThegoodknowledgeonBasicCDIandSPI3. Lackofinformation(CDIisoftenreducedtoabasicDIsolution)

Extensions,whatfor?

Tointegrate3rdpartylibraries,frameworksorlegacycomponents

Tochangeexistingconfigurationorbehavior

ToextendCDIandJavaEE

Thankstothem,JavaEEcanevolvebetweenmajorreleases

Extensions,how?

ObservingSPIeventsatboottimerelatedtothebeanmanagerlifecycle

Checkingwhatmeta-dataarebeingcreated

Modifyingthesemeta-dataorcreatingnewones

Moreconcretely

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

Justputthefullyqualifiednameofyourextensionclassinthisfile

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

publicclassCdiExtensionimplementsExtension{

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

voidafterDeploymentValidation(@ObservesAfterDeploymentValidationadv){}}

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

Example:IgnoringJPAentities

ThefollowingextensionpreventsCDItomanageentities

Thisisacommonlyadmittedgoodpractice

publicclassVetoEntityimplementsExtension{

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

ExtensionsarelaunchedduringbootstrapandarebasedonCDIevents

Oncetheapplicationisbootstrapped,theBeanManagerisinread-onlymode(noruntimebeanregistration)

Youonlyhaveto@Observes built-inCDIeventstocreateyourextensions

Remember

Howtointegratea3rdpartyLibrary(DropwizardMetrics)intotheCDIProgrammingModel

3rdpartyLibrary

AboutDropwizardMetrics

Providesdifferentmetrictypes: Counter , Gauge , Meter , Timer ,…

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

Providesa MetricRegistry whichcollectsallyourappmetrics

ProvidesannotationsforAOPframeworks: @Counted , @Timed ,…

… butdoesnotincludeintegrationwiththeseframeworks

Moreatdropwizard.github.io/metrics

DiscoverhowwecreatedCDIintegrationmoduleforMetrics

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

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

classTimedMethodBean{@InjectMetricRegistryregistry;

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

WecouldhavealotmorewithadvancedCDIfeatures

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

StepstoapplyatimerinAOPstyle

Createaninterceptorforthetimertechnicalcode

MaketheMetricsannotation @Timed avalidinterceptorbindingannotation

Programmaticallyadd @Timed asaninterceptorbinding

Usethemagic

Preparinginterceptorcreation

Tocreateaninterceptorweshouldstartbydetectingthe"technicalcode"thatwillwrapthe"businesscode"

classTimedMethodBean{

@InjectMetricRegistryregistry;

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

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

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

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

Backoninterceptorbinding

Aninterceptorbindingisanannotationusedin2kindofplaces:

1. Ontheinterceptordefinitionstoassociatethemtothisannotation2. Onthemethods/classestobeinterceptedbythisinterceptor

Aninterceptorbindingshouldbeannotatedwiththe@InterceptorBinding metaannotationorshouldbedeclaredasaninterceptorbindingprogrammatically

Iftheinterceptorbindingannotationhasmembers:

1. Theirvaluesaretakenintoaccounttodistinguishtwoinstances2. Unlessmembersareannotatedwith @NonBinding

@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

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!

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

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

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

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

Firstgoalachieved

Wecannowwrite:

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

AndhaveaMetricsTimer appliedtothemethod

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

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

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

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

Adding @Metric tothelistofqualifiers

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

Sowedon’thavetoadd @Nonbinding annotationtothem

publicclassMetricExtensionimplementsExtension{

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

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;}//...

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

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

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

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

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 )

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

TestyourCDIknowledge

Quizztime

FindthevalidinjectionspointsclassMySuperBean{

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

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

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

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

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

SolutionclassMySuperBean{

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

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

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

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

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

FindBeanscandidateswithout beans.xml injar(CDI1.2)

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

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

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

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

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

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

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

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

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

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

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

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

Findthevalidproducers@ApplicationScopedpublicclassMyBean{

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

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

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

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

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[]}

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[]}

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]}

HowtouseCDIasdependencyinjectioncontainerforanintegrationframework(ApacheCamel)

CamelCDI

AboutApacheCamel

Open-sourceintegrationframeworkbasedonknownEnterpriseIntegrationPatterns

ProvidesavarietyofDSLstowriteroutingandmediationrules

ProvidessupportforbeanbindingandseamlessintegrationwithDIframeworks

DiscoverhowwecreatedCDIintegrationmoduleforCamel

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

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

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

BasicCDIintegration(3/3)@ApplicationScopedclassCamelContextBeanextendsDefaultCamelContext{

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

WecouldhavealotmorewithadvancedCDIfeatures

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;

StepstointegrateCamelandCDI

Managethecreationandtheconfigurationofthe CamelContextbean

Bindthe CamelContext lifecyclethatoftheCDIcontainer

ImplementtheCamelSPItolookupCDIbeanreferences

Useacustom InjectionTarget forCDIbeanscontainingCamelannotations

Usethemagic

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

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

Addingthe CamelContext bean

Automaticallyadda CamelContext beaninthedeploymentarchive

Howtoaddabeanprogrammatically?

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

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

Addingaprogrammaticbeantothedeployment

Thenaddthe CamelContextBean beanprogrammaticallybyobservingthe AfterBeanDiscovery lifecyleevent

publicclassCamelExtensionimplementsExtension{

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

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

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

Firstgoalachieved

Wecangetridofthefollowingcode:

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

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

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

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

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

Secondgoalachieved2/3

Declarethe properties componentwiththe @Named qualifier

classPropertiesComponentFactoryBean{

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

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

Thirdgoal:SupportCamelannotationsinCDIbeans

CamelprovidesasetofDIframeworkagnosticannotationsforresourceinjection

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

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

@BeanInject("foo")FooBeanfoo;

Howtosupportcustomannotationsinjection?

Howtosupportcustomannotationsinjection?

Createacustom InjectionTarget thatusesthedefaultCamelbeanpostprocessor DefaultCamelBeanPostProcessor

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

HookitintotheCDIinjectionmechanismbyobservingtheProcessInjectionTarget lifecycleevent

OnlyforbeanscontainingCamelannotationsbyobservingtheProcessAnnotatedType lifecycleandusing @WithAnnotations

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

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

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

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

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

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

Howtoachievethis?

WecancreateaCDIqualifiertoholdtheCamelnodeidmetadata:

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

andcreateanextensionthatwill:

1. DetecttheCDIbeanscontainingobservermethodswiththe @NodeIdqualifierbyobservingthe ProcessObserverMethod eventandcollecttheCamelprocessornodestobeinstrumented

2. CustomizetheCamelcontextbyprovidinganimplementationoftheCamel InterceptStrategy interfacethatwillfireaCDIeventeachtimean Exchange isprocessedbytheinstrumentednodes

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

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

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

Conclusion

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

AntoineSabot-DurandAntoninStefanutti@antoine_sd@astefanut

Annexes

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>

top related