Skip to content

Commit

Permalink
Merge pull request #6098 from mkouba/issue-6024
Browse files Browse the repository at this point in the history
ArC - introduce ObserverTransformer build extension
  • Loading branch information
mkouba authored Dec 12, 2019
2 parents 7789ce6 + 2c78d5d commit 5dc868f
Show file tree
Hide file tree
Showing 21 changed files with 989 additions and 360 deletions.
42 changes: 37 additions & 5 deletions docs/src/main/asciidoc/cdi-reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -554,17 +554,18 @@ The following sample shows how to apply transformation to injection points with
[source,java]
----
@BuildStep
InjectionPointTransformerBuildItem transform() {
InjectionPointTransformerBuildItem transformer() {
return new InjectionPointTransformerBuildItem(new InjectionPointsTransformer() {
public boolean appliesTo(Type requiredType) {
return requiredType.equals(Type.create(DotName.createSimple(Foo.class.getName()), Type.Kind.CLASS));
return requiredType.name().equals(DotName.createSimple(Foo.class.getName()));
}
public void transform(TransformationContext transformationContext) {
if (transformationContext.getQualifiers().stream()
public void transform(TransformationContext context) {
if (context.getQualifiers().stream()
.anyMatch(a -> a.name().equals(DotName.createSimple(MyQualifier.class.getName())))) {
transformationContext.transform().removeAll()
context.transform()
.removeAll()
.add(DotName.createSimple(MyOtherQualifier.class.getName()))
.done();
}
Expand All @@ -573,6 +574,35 @@ InjectionPointTransformerBuildItem transform() {
}
----

=== Observer Transformation

Any https://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#observer_methods[observer method] definition can be vetoed or transformed using an `ObserverTransformerBuildItem`.
The attributes that can be transformed include:

- https://docs.jboss.org/cdi/api/2.0/javax/enterprise/inject/spi/ObserverMethod.html#getObservedQualifiers--[qualifiers]
- https://docs.jboss.org/cdi/api/2.0/javax/enterprise/inject/spi/ObserverMethod.html#getReception--[reception]
- https://docs.jboss.org/cdi/api/2.0/javax/enterprise/inject/spi/ObserverMethod.html#getPriority--[priority]
- https://docs.jboss.org/cdi/api/2.0/javax/enterprise/inject/spi/ObserverMethod.html#getTransactionPhase--[transaction phase]
- https://docs.jboss.org/cdi/api/2.0/javax/enterprise/inject/spi/ObserverMethod.html#isAsync--[asynchronous]

[source,java]
----
@BuildStep
ObserverTransformerBuildItem transformer() {
return new ObserverTransformerBuildItem(new ObserverTransformer() {
public boolean appliesTo(Type observedType, Set<AnnotationInstance> qualifiers) {
return observedType.name.equals(DotName.createSimple(MyEvent.class.getName()));
}
public void transform(TransformationContext context) {
// Veto all observers of MyEvent
context.veto();
}
});
}
----

=== Bean Deployment Validation

Once the bean deployment is ready an extension can perform additional validations and inspect the found beans, observers and injection points.
Expand Down Expand Up @@ -682,6 +712,8 @@ Here is a summary of which extensions can access which metadata:
** Has access to `ANNOTATION_STORE`
* `InjectionPointsTransformer`
** Has access to `ANNOTATION_STORE`, `QUALIFIERS`, `INTERCEPTOR_BINDINGS`, `STEREOTYPES`
* `ObserverTransformer`
** Has access to `ANNOTATION_STORE`, `QUALIFIERS`, `INTERCEPTOR_BINDINGS`, `STEREOTYPES`
* `BeanRegistrar`
** Has access to `ANNOTATION_STORE`, `QUALIFIERS`, `INTERCEPTOR_BINDINGS`, `STEREOTYPES`, `BEANS`
* `BeanDeploymentValidator`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ public ContextRegistrationPhaseBuildItem initialize(
ApplicationArchivesBuildItem applicationArchivesBuildItem,
List<AnnotationsTransformerBuildItem> annotationTransformers,
List<InjectionPointTransformerBuildItem> injectionPointTransformers,
List<ObserverTransformerBuildItem> observerTransformers,
List<InterceptorBindingRegistrarBuildItem> interceptorBindingRegistrarBuildItems,
List<AdditionalStereotypeBuildItem> additionalStereotypeBuildItems,
List<ApplicationClassPredicateBuildItem> applicationClassPredicates,
Expand Down Expand Up @@ -172,12 +173,16 @@ public void transform(TransformationContext transformationContext) {
builder.addResourceAnnotations(
resourceAnnotations.stream().map(ResourceAnnotationBuildItem::getName).collect(Collectors.toList()));
// register all annotation transformers
for (AnnotationsTransformerBuildItem transformerItem : annotationTransformers) {
builder.addAnnotationTransformer(transformerItem.getAnnotationsTransformer());
for (AnnotationsTransformerBuildItem transformer : annotationTransformers) {
builder.addAnnotationTransformer(transformer.getAnnotationsTransformer());
}
// register all injection point transformers
for (InjectionPointTransformerBuildItem transformerItem : injectionPointTransformers) {
builder.addInjectionPointTransformer(transformerItem.getInjectionPointsTransformer());
for (InjectionPointTransformerBuildItem transformer : injectionPointTransformers) {
builder.addInjectionPointTransformer(transformer.getInjectionPointsTransformer());
}
// register all observer transformers
for (ObserverTransformerBuildItem transformer : observerTransformers) {
builder.addObserverTransformer(transformer.getInstance());
}
// register additional interceptor bindings
for (InterceptorBindingRegistrarBuildItem bindingRegistrar : interceptorBindingRegistrarBuildItems) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.quarkus.arc.deployment;

import io.quarkus.arc.processor.ObserverTransformer;
import io.quarkus.builder.item.MultiBuildItem;

/**
* This build item is used to register an {@link ObserverTransformer} instance.
*/
public final class ObserverTransformerBuildItem extends MultiBuildItem {

private final ObserverTransformer transformer;

public ObserverTransformerBuildItem(ObserverTransformer transformer) {
this.transformer = transformer;
}

public ObserverTransformer getInstance() {
return transformer;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package io.quarkus.arc.test.observer;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static org.junit.jupiter.api.Assertions.assertEquals;

import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;

import javax.enterprise.event.Event;
import javax.enterprise.event.Observes;
import javax.inject.Qualifier;
import javax.inject.Singleton;

import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.DotName;
import org.jboss.jandex.Type;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import com.google.inject.Inject;

import io.quarkus.arc.deployment.ObserverTransformerBuildItem;
import io.quarkus.arc.processor.ObserverTransformer;
import io.quarkus.builder.BuildChainBuilder;
import io.quarkus.builder.BuildContext;
import io.quarkus.builder.BuildStep;
import io.quarkus.test.QuarkusUnitTest;

public class ObserverTransformerTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(MyObserver.class, AlphaQualifier.class, BravoQualifier.class))
.addBuildChainCustomizer(buildCustomizer());

static Consumer<BuildChainBuilder> buildCustomizer() {
return new Consumer<BuildChainBuilder>() {

@Override
public void accept(BuildChainBuilder builder) {
builder.addBuildStep(new BuildStep() {

@Override
public void execute(BuildContext context) {
context.produce(new ObserverTransformerBuildItem(new ObserverTransformer() {

@Override
public boolean appliesTo(Type observedType, Set<AnnotationInstance> qualifiers) {
return observedType.name().equals(DotName.createSimple(MyEvent.class.getName()));
}

@Override
public void transform(TransformationContext context) {
if (context.getMethod().name().equals("")) {
context.transform().removeAll().done();
}
}

}));
}
}).produces(ObserverTransformerBuildItem.class).build();
}
};
}

@BravoQualifier
@Inject
Event<MyEvent> event;

@Test
public void testTransformation() {
MyEvent myEvent = new MyEvent();
event.fire(myEvent);
// MyObserver.onMyEventRemoveQualifiers() would not match without transformation
assertEquals(1, myEvent.log.size());
assertEquals("onMyEventRemoveQualifiers", myEvent.log.get(0));
}

@Singleton
static class MyObserver {

void onMyEventRemoveQualifiers(@Observes @BravoQualifier MyEvent event) {
event.log.add("onMyEventRemoveQualifiers");
}

}

@Qualifier
@Inherited
@Target({ TYPE, METHOD, FIELD, PARAMETER })
@Retention(RUNTIME)
public @interface AlphaQualifier {

}

@Qualifier
@Inherited
@Target({ TYPE, METHOD, FIELD, PARAMETER })
@Retention(RUNTIME)
public @interface BravoQualifier {

}

static class MyEvent {

final List<String> log = new ArrayList<>();

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@
import org.eclipse.microprofile.faulttolerance.FallbackHandler;
import org.eclipse.microprofile.faulttolerance.Retry;
import org.eclipse.microprofile.faulttolerance.Timeout;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.AnnotationTarget.Kind;
import org.jboss.jandex.AnnotationValue;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.IndexView;
import org.jboss.jandex.Type;

import com.netflix.hystrix.HystrixCircuitBreaker;

Expand All @@ -34,10 +36,14 @@
import io.quarkus.arc.deployment.BeanArchiveIndexBuildItem;
import io.quarkus.arc.deployment.BeanContainerBuildItem;
import io.quarkus.arc.deployment.BeanDefiningAnnotationBuildItem;
import io.quarkus.arc.deployment.ObserverTransformerBuildItem;
import io.quarkus.arc.deployment.ValidationPhaseBuildItem;
import io.quarkus.arc.processor.Annotations;
import io.quarkus.arc.processor.AnnotationsTransformer;
import io.quarkus.arc.processor.BeanInfo;
import io.quarkus.arc.processor.BuiltinScope;
import io.quarkus.arc.processor.DotNames;
import io.quarkus.arc.processor.ObserverTransformer;
import io.quarkus.deployment.Capabilities;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
Expand All @@ -51,6 +57,7 @@
import io.quarkus.deployment.builditem.nativeimage.NativeImageSystemPropertyBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.deployment.logging.LogCleanupFilterBuildItem;
import io.quarkus.smallrye.faulttolerance.runtime.HystrixInitializerStarter;
import io.quarkus.smallrye.faulttolerance.runtime.NoopMetricRegistry;
import io.quarkus.smallrye.faulttolerance.runtime.QuarkusFallbackHandlerProvider;
import io.quarkus.smallrye.faulttolerance.runtime.QuarkusFaultToleranceOperationProvider;
Expand All @@ -64,6 +71,8 @@

public class SmallRyeFaultToleranceProcessor {

static final DotName HYSTRIX_INITIALIZER_NAME = DotName.createSimple(HystrixInitializer.class.getName());

@Inject
BuildProducer<ReflectiveClassBuildItem> reflectiveClass;

Expand Down Expand Up @@ -148,7 +157,8 @@ public void transform(TransformationContext context) {
DefaultHystrixConcurrencyStrategy.class,
QuarkusFaultToleranceOperationProvider.class, QuarkusFallbackHandlerProvider.class,
DefaultCommandListenersProvider.class,
MetricsCollectorFactory.class);
MetricsCollectorFactory.class,
HystrixInitializerStarter.class);
additionalBean.produce(builder.build());

if (!capabilities.isCapabilityPresent(Capabilities.METRICS)) {
Expand Down Expand Up @@ -230,4 +240,26 @@ public void clearStatic(SmallryeFaultToleranceRecorder recorder, ShutdownContext
// this is needed so that shutdown context of FT is executed before Arc container shuts down
recorder.resetCommandContextOnUndeploy(context);
}

@BuildStep
ObserverTransformerBuildItem vetoHystrixInitializerObserver() {
return new ObserverTransformerBuildItem(new ObserverTransformer() {

@Override
public boolean appliesTo(Type observedType, Set<AnnotationInstance> qualifiers) {
AnnotationInstance initialized = Annotations.find(Annotations.getParameterAnnotations(qualifiers),
DotNames.INITIALIZED);
return initialized != null ? initialized.value().asClass().name().equals(BuiltinScope.APPLICATION.getName())
: false;
}

@Override
public void transform(TransformationContext context) {
if (context.getMethod().declaringClass().name().equals(HYSTRIX_INITIALIZER_NAME)) {
context.veto();
}
}

});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.quarkus.smallrye.faulttolerance.runtime;

import javax.enterprise.context.Dependent;
import javax.enterprise.event.Observes;

import io.quarkus.runtime.StartupEvent;
import io.smallrye.faulttolerance.HystrixInitializer;

@Dependent
public class HystrixInitializerStarter {

/**
* This is a replacement for <code>io.smallrye.faulttolerance.HystrixInitializer.init(Object)</code> observer method which
* is vetoed because we don't want initialize Hystrix during static init.
*
* @param event
* @param initializer
*/
void startup(@Observes StartupEvent event, HystrixInitializer initializer) {
// HystrixInitializer is a normal scoped bean so we have to invoke a method upon the injected proxy to force the instantiation
initializer.toString();
}

}
Loading

0 comments on commit 5dc868f

Please sign in to comment.