Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ArC - support CDI decorators #16010

Merged
merged 1 commit into from
Mar 31, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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