Skip to content

Commit

Permalink
ArC - support CDI decorators
Browse files Browse the repository at this point in the history
- resolves #15490

Update independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java

Co-authored-by: Ladislav Thon <[email protected]>

Update docs/src/main/asciidoc/cdi.adoc

Co-authored-by: Matej Novotny <[email protected]>
  • Loading branch information
mkouba and manovotn committed Mar 30, 2021
1 parent 08f87c9 commit d4dd21b
Show file tree
Hide file tree
Showing 38 changed files with 2,384 additions and 170 deletions.
2 changes: 1 addition & 1 deletion docs/src/main/asciidoc/cdi-reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -200,13 +200,13 @@ public class CounterBean {
* Interceptors
** Business method interceptors: `@AroundInvoke`
** Interceptors for lifecycle event callbacks: `@PostConstruct`, `@PreDestroy`, `@AroundConstruct`
* Decorators
* Events and observer methods, including asynchronous events and transactional observer methods

[[limitations]]
== Limitations

* `@ConversationScoped` is not supported
* Decorators are not supported
* Portable Extensions are not supported
* `BeanManager` - only the following methods are implemented: `getBeans()`, `createCreationalContext()`, `getReference()`, `getInjectableReference()` , `resolve()`, `getContext()`, `fireEvent()`, `getEvent()` and `createInstance()`
* Specialization is not supported
Expand Down
48 changes: 48 additions & 0 deletions docs/src/main/asciidoc/cdi.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,54 @@ public class LoggingInterceptor {
NOTE: Instances of interceptors are dependent objects of the bean instance they intercept, i.e. a new interceptor instance is created for each intercepted bean.
[[decorators]]
=== Decorators
Decorators are similar to interceptors, but because they implement interfaces with business semantics, they are able to implement business logic.
.Simple Decorator Example
[source,java]
----
import javax.decorator.Decorator;
import javax.decorator.Delegate;
import javax.annotation.Priority;
import javax.inject.Inject;
import javax.enterprise.inject.Any;

public interface Account {
void withdraw(BigDecimal amount);
}

@Priority(10) <1>
@Decorator <2>
public class LargeTxAccount implements Account { <3>

@Inject
@Any
@Delegate
Account delegate; <4>

@Inject
LogService logService; <5>

void withdraw(BigDecimal amount) {
delegate.withdraw(amount); <6>
if (amount.compareTo(1000) > 0) {
logService.logWithdrawal(delegate, amount);
}
}

}
----
<1> `@Priority` enables the decorator. Decorators with smaller priority values are called first.
<2> `@Decorator` marks a decorator component.
<3> The set of decorated types includes all bean types which are Java interfaces, except for `java.io.Serializable`.
<4> Each decorator must declare exactly one _delegate injection point_. The decorator applies to beans that are assignable to this delegate injection point.
<5> Decorators can inject other beans.
<6> The decorator may invoke any method of the delegate object. And the container invokes either the next decorator in the chain or the business method of the intercepted instance.
NOTE: Instances of decorators are dependent objects of the bean instance they intercept, i.e. a new decorator instance is created for each intercepted bean.
=== Events and Observers
Beans may also produce and consume events to interact in a completely decoupled fashion.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public class BeanDeployment {
private final List<BeanInfo> beans;

private final List<InterceptorInfo> interceptors;
private final List<DecoratorInfo> decorators;

private final List<ObserverInfo> observers;

Expand Down Expand Up @@ -178,6 +179,7 @@ public class BeanDeployment {

this.injectionPoints = new CopyOnWriteArrayList<>();
this.interceptors = new CopyOnWriteArrayList<>();
this.decorators = new CopyOnWriteArrayList<>();
this.beans = new CopyOnWriteArrayList<>();
this.observers = new CopyOnWriteArrayList<>();

Expand Down Expand Up @@ -233,6 +235,7 @@ BeanRegistrar.RegistrationContext registerBeans(List<BeanRegistrar> beanRegistra
buildContextPut(Key.OBSERVERS.asString(), Collections.unmodifiableList(observers));

this.interceptors.addAll(findInterceptors(injectionPoints));
this.decorators.addAll(findDecorators(injectionPoints));
this.injectionPoints.addAll(injectionPoints);
buildContextPut(Key.INJECTION_POINTS.asString(), Collections.unmodifiableList(this.injectionPoints));

Expand All @@ -254,6 +257,10 @@ void init(Consumer<BytecodeTransformer> bytecodeTransformerConsumer,
for (InterceptorInfo interceptor : interceptors) {
interceptor.init(errors, bytecodeTransformerConsumer, transformUnproxyableClasses);
}
for (DecoratorInfo decorator : decorators) {
decorator.init(errors, bytecodeTransformerConsumer, transformUnproxyableClasses);
}

processErrors(errors);
List<Predicate<BeanInfo>> allUnusedExclusions = new ArrayList<>(additionalUnusedBeanExclusions);
if (unusedExclusions != null) {
Expand Down Expand Up @@ -391,6 +398,10 @@ public Collection<InterceptorInfo> getInterceptors() {
return Collections.unmodifiableList(interceptors);
}

public Collection<DecoratorInfo> getDecorators() {
return Collections.unmodifiableList(decorators);
}

public Collection<StereotypeInfo> getStereotypes() {
return Collections.unmodifiableCollection(stereotypes.values());
}
Expand Down Expand Up @@ -1128,6 +1139,33 @@ private List<InterceptorInfo> findInterceptors(List<InjectionPointInfo> injectio
return interceptors;
}

private List<DecoratorInfo> findDecorators(List<InjectionPointInfo> injectionPoints) {
Map<DotName, ClassInfo> decoratorClasses = new HashMap<>();
for (AnnotationInstance annotation : beanArchiveIndex.getAnnotations(DotNames.DECORATOR)) {
if (Kind.CLASS.equals(annotation.target().kind())) {
decoratorClasses.put(annotation.target().asClass().name(), annotation.target().asClass());
}
}
List<DecoratorInfo> decorators = new ArrayList<>();
for (ClassInfo decoratorClass : decoratorClasses.values()) {
if (annotationStore.hasAnnotation(decoratorClass, DotNames.VETOED) || isExcluded(decoratorClass)) {
// Skip vetoed decorators
continue;
}
decorators
.add(Decorators.createDecorator(decoratorClass, this, injectionPointTransformer, annotationStore));
}
if (LOGGER.isTraceEnabled()) {
for (DecoratorInfo decorator : decorators) {
LOGGER.logf(Level.TRACE, "Created %s", decorator);
}
}
for (DecoratorInfo decorator : decorators) {
injectionPoints.addAll(decorator.getAllInjectionPoints());
}
return decorators;
}

private void validateBeans(List<Throwable> errors, List<BeanDeploymentValidator> validators,
Consumer<BytecodeTransformer> bytecodeTransformerConsumer) {

Expand Down
Loading

0 comments on commit d4dd21b

Please sign in to comment.