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 fixes for spec compatibility, round 8 #33051

Merged
merged 6 commits into from
May 15, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
import java.util.List;
import java.util.stream.Collectors;

import jakarta.enterprise.context.Dependent;
import jakarta.enterprise.inject.spi.DeploymentException;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
Expand All @@ -36,7 +36,7 @@ public void testFailure() {
fail();
}

@Singleton
@Dependent
static class Foo<T> {

@Inject
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import java.util.function.Consumer;
import java.util.function.Supplier;

import jakarta.enterprise.context.Dependent;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.WebApplicationException;
Expand Down Expand Up @@ -137,6 +138,7 @@ public interface HasMessage {
}

@Provider
@Dependent
public static class HasMessageMessageBodyWriter<T extends HasMessage> implements ServerMessageBodyWriter<T> {

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1422,7 +1422,10 @@ private List<InterceptorInfo> findInterceptors(List<InjectionPointInfo> injectio
// Skip vetoed interceptors
continue;
}
interceptors.add(Interceptors.createInterceptor(interceptorClass, this, injectionPointTransformer));
InterceptorInfo interceptor = Interceptors.createInterceptor(interceptorClass, this, injectionPointTransformer);
if (interceptor != null) {
interceptors.add(interceptor);
}
}
if (LOGGER.isTraceEnabled()) {
for (InterceptorInfo interceptor : interceptors) {
Expand All @@ -1448,7 +1451,10 @@ private List<DecoratorInfo> findDecorators(List<InjectionPointInfo> injectionPoi
// Skip vetoed decorators
continue;
}
decorators.add(Decorators.createDecorator(decoratorClass, this, injectionPointTransformer));
DecoratorInfo decorator = Decorators.createDecorator(decoratorClass, this, injectionPointTransformer);
if (decorator != null) {
decorators.add(decorator);
}
}
if (LOGGER.isTraceEnabled()) {
for (DecoratorInfo decorator : decorators) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1276,6 +1276,12 @@ BeanInfo create() {
} else {
scope = scopes.get(0);
}
if (scope != null // `null` is just like `@Dependent`
&& !BuiltinScope.DEPENDENT.is(scope)
&& !beanClass.typeParameters().isEmpty()) {
throw new DefinitionException(
"Declaring class of a managed bean is generic, its scope must be @Dependent: " + beanClass);
}
if (!isAlternative) {
isAlternative = initStereotypeAlternative(stereotypes, beanDeployment);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,14 @@ Collection<Resource> generate(String name, BeanDeployment beanDeployment, Map<Be
getComponents.invokeInterfaceMethod(MethodDescriptors.LIST_ADD, contextsHandle, contextHandle);
}

// All interceptor bindings
ResultHandle interceptorBindings = getComponents.newInstance(MethodDescriptor.ofConstructor(HashSet.class));
for (ClassInfo binding : beanDeployment.getInterceptorBindings()) {
getComponents.invokeInterfaceMethod(MethodDescriptors.SET_ADD, interceptorBindings,
getComponents.load(binding.name().toString()));
}

// Transitive interceptor bindings
ResultHandle transitiveBindingsHandle = getComponents.newInstance(MethodDescriptor.ofConstructor(HashMap.class));
for (Entry<DotName, Set<AnnotationInstance>> entry : beanDeployment.getTransitiveInterceptorBindings().entrySet()) {
ResultHandle bindingsHandle = getComponents.newInstance(MethodDescriptor.ofConstructor(HashSet.class));
Expand Down Expand Up @@ -160,9 +168,9 @@ Collection<Resource> generate(String name, BeanDeployment beanDeployment, Map<Be

ResultHandle componentsHandle = getComponents.newInstance(
MethodDescriptor.ofConstructor(Components.class, Collection.class, Collection.class, Collection.class,
Map.class, Supplier.class, Map.class, Set.class),
beansHandle, observersHandle, contextsHandle, transitiveBindingsHandle, removedBeansSupplier.getInstance(),
qualifiersNonbindingMembers, qualifiers);
Set.class, Map.class, Supplier.class, Map.class, Set.class),
beansHandle, observersHandle, contextsHandle, interceptorBindings, transitiveBindingsHandle,
removedBeansSupplier.getInstance(), qualifiersNonbindingMembers, qualifiers);
getComponents.returnValue(componentsHandle);

// Finally write the bytecode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ final class Decorators {
private Decorators() {
}

/**
*
* @param decoratorClass
* @param beanDeployment
* @param transformer
* @return a new decorator info, or (only in strict mode) {@code null} if the decorator is disabled
*/
static DecoratorInfo createDecorator(ClassInfo decoratorClass, BeanDeployment beanDeployment,
InjectionPointModifier transformer) {

Expand Down Expand Up @@ -100,6 +107,11 @@ static DecoratorInfo createDecorator(ClassInfo decoratorClass, BeanDeployment be
}

if (priority == null) {
if (beanDeployment.strictCompatibility) {
// decorator without `@Priority` is disabled per the specification
return null;
}

LOGGER.info("The decorator " + decoratorClass + " does not declare any @Priority. " +
"It will be assigned a default priority value of 0.");
priority = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ private Interceptors() {
*
* @param interceptorClass
* @param beanDeployment
* @return a new interceptor info
* @return a new interceptor info, or (only in strict mode) {@code null} if the interceptor is disabled
*/
static InterceptorInfo createInterceptor(ClassInfo interceptorClass, BeanDeployment beanDeployment,
InjectionPointModifier transformer) {
Expand Down Expand Up @@ -60,6 +60,11 @@ static InterceptorInfo createInterceptor(ClassInfo interceptorClass, BeanDeploym
}

if (priority == null) {
if (beanDeployment.strictCompatibility) {
// interceptor without `@Priority` is disabled per the specification
return null;
}

LOGGER.info("The interceptor " + interceptorClass + " does not declare any @Priority. " +
"It will be assigned a default priority value of 0.");
priority = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,21 @@ public final class Components {
private final Supplier<Collection<RemovedBean>> removedBeans;
private final Collection<InjectableObserverMethod<?>> observers;
private final Collection<InjectableContext> contexts;
private final Set<String> interceptorBindings;
private final Map<Class<? extends Annotation>, Set<Annotation>> transitiveInterceptorBindings;
private final Map<String, Set<String>> qualifierNonbindingMembers;
private final Set<String> qualifiers;

public Components(Collection<InjectableBean<?>> beans, Collection<InjectableObserverMethod<?>> observers,
Collection<InjectableContext> contexts,
Set<String> interceptorBindings,
Map<Class<? extends Annotation>, Set<Annotation>> transitiveInterceptorBindings,
Supplier<Collection<RemovedBean>> removedBeans, Map<String, Set<String>> qualifierNonbindingMembers,
Set<String> qualifiers) {
this.beans = beans;
this.observers = observers;
this.contexts = contexts;
this.interceptorBindings = interceptorBindings;
this.transitiveInterceptorBindings = transitiveInterceptorBindings;
this.removedBeans = removedBeans;
this.qualifierNonbindingMembers = qualifierNonbindingMembers;
Expand All @@ -42,6 +45,10 @@ public Collection<InjectableContext> getContexts() {
return contexts;
}

public Set<String> getInterceptorBindings() {
return interceptorBindings;
}

public Map<Class<? extends Annotation>, Set<Annotation>> getTransitiveInterceptorBindings() {
return transitiveInterceptorBindings;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package io.quarkus.arc;

import java.lang.annotation.Annotation;
import java.util.Set;

import jakarta.enterprise.inject.spi.Decorator;

import io.quarkus.arc.impl.Qualifiers;

/**
* Quarkus representation of a decorator bean.
* This interface extends the standard CDI {@link Decorator} interface.
Expand All @@ -13,4 +18,8 @@ default Kind getKind() {
return Kind.DECORATOR;
}

@Override
default Set<Annotation> getDelegateQualifiers() {
return Qualifiers.DEFAULT_QUALIFIERS;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -83,7 +84,6 @@ public class ArcContainerImpl implements ArcContainer {
private final List<InjectableInterceptor<?>> interceptors;
private final List<InjectableDecorator<?>> decorators;
private final List<InjectableObserverMethod<?>> observers;
private final Map<Class<? extends Annotation>, Set<Annotation>> transitiveInterceptorBindings;
private final Contexts contexts;
private final ComputingCache<Resolvable, Set<InjectableBean<?>>> resolved;
private final ComputingCache<String, InjectableBean<?>> beansById;
Expand All @@ -93,6 +93,7 @@ public class ArcContainerImpl implements ArcContainer {

final InstanceImpl<Object> instance;
final Qualifiers registeredQualifiers;
final InterceptorBindings registeredInterceptorBindings;

private volatile ExecutorService executorService;

Expand All @@ -109,6 +110,7 @@ public ArcContainerImpl(CurrentContextFactory currentContextFactory, boolean str
List<InjectableInterceptor<?>> interceptors = new ArrayList<>();
List<InjectableDecorator<?>> decorators = new ArrayList<>();
List<InjectableObserverMethod<?>> observers = new ArrayList<>();
Set<String> interceptorBindings = new HashSet<>();
Map<Class<? extends Annotation>, Set<Annotation>> transitiveInterceptorBindings = new HashMap<>();
Map<String, Set<String>> qualifierNonbindingMembers = new HashMap<>();
Set<String> qualifiers = new HashSet<>();
Expand All @@ -132,6 +134,7 @@ public ArcContainerImpl(CurrentContextFactory currentContextFactory, boolean str
}
removedBeans.add(c.getRemovedBeans());
observers.addAll(c.getObservers());
interceptorBindings.addAll(c.getInterceptorBindings());
transitiveInterceptorBindings.putAll(c.getTransitiveInterceptorBindings());
qualifierNonbindingMembers.putAll(c.getQualifierNonbindingMembers());
qualifiers.addAll(c.getQualifiers());
Expand All @@ -140,7 +143,8 @@ public ArcContainerImpl(CurrentContextFactory currentContextFactory, boolean str
// register built-in beans
addBuiltInBeans(beans);

interceptors.sort((i1, i2) -> Integer.compare(i2.getPriority(), i1.getPriority()));
interceptors.sort(Comparator.comparingInt(InjectableInterceptor::getPriority));
decorators.sort(Comparator.comparingInt(InjectableDecorator::getPriority));

resolved = new ComputingCache<>(this::resolve);
beansById = new ComputingCache<>(this::findById);
Expand Down Expand Up @@ -168,16 +172,17 @@ public List<RemovedBean> get() {
return List.copyOf(removed);
}
});
this.transitiveInterceptorBindings = Map.copyOf(transitiveInterceptorBindings);
this.registeredQualifiers = new Qualifiers(qualifiers, qualifierNonbindingMembers);
this.registeredInterceptorBindings = new InterceptorBindings(interceptorBindings, transitiveInterceptorBindings);

Contexts.Builder contextsBuilder = new Contexts.Builder(
new RequestContext(this.currentContextFactory.create(RequestScoped.class),
notifierOrNull(Set.of(Initialized.Literal.REQUEST, Any.Literal.INSTANCE)),
notifierOrNull(Set.of(BeforeDestroyed.Literal.REQUEST, Any.Literal.INSTANCE)),
notifierOrNull(Set.of(Destroyed.Literal.REQUEST, Any.Literal.INSTANCE))),
new ApplicationContext(),
new SingletonContext());
new SingletonContext(),
new DependentContext());

// Add custom contexts
for (Components c : components) {
Expand Down Expand Up @@ -540,10 +545,6 @@ Set<Bean<?>> getBeans(String name) {
return new HashSet<>(getMatchingBeans(name));
}

Map<Class<? extends Annotation>, Set<Annotation>> getTransitiveInterceptorBindings() {
return transitiveInterceptorBindings;
}

boolean isScope(Class<? extends Annotation> annotationType) {
if (annotationType.isAnnotationPresent(Scope.class) || annotationType.isAnnotationPresent(NormalScope.class)) {
return true;
Expand Down Expand Up @@ -588,6 +589,11 @@ private InjectableBean<?> findById(String identifier) {
return interceptorBean;
}
}
for (InjectableDecorator<?> decoratorBean : decorators) {
if (decoratorBean.getIdentifier().equals(identifier)) {
return decoratorBean;
}
}
return null;
}

Expand Down Expand Up @@ -794,11 +800,12 @@ List<Interceptor<?>> resolveInterceptors(InterceptionType type, Annotation... in
if (interceptorBindings.length == 0) {
throw new IllegalArgumentException("No interceptor bindings");
}
registeredInterceptorBindings.verify(interceptorBindings);
List<Interceptor<?>> interceptors = new ArrayList<>();
List<Annotation> bindings = new ArrayList<>();
for (Annotation binding : interceptorBindings) {
bindings.add(binding);
Set<Annotation> transitive = transitiveInterceptorBindings.get(binding.annotationType());
Set<Annotation> transitive = registeredInterceptorBindings.getTransitive(binding.annotationType());
if (transitive != null) {
bindings.addAll(transitive);
}
Expand All @@ -818,10 +825,14 @@ List<Decorator<?>> resolveDecorators(Set<Type> types, Annotation... qualifiers)
if (Objects.requireNonNull(types).isEmpty()) {
throw new IllegalArgumentException("The set of bean types must not be empty");
}
if (qualifiers == null || qualifiers.length == 0) {
qualifiers = new Annotation[] { Default.Literal.INSTANCE };
} else {
registeredQualifiers.verify(qualifiers);
}
List<Decorator<?>> decorators = new ArrayList<>();
for (InjectableDecorator<?> decorator : this.decorators) {
if (decoratorMatches(types, Set.of(qualifiers), decorator.getDelegateType(),
decorator.getDelegateQualifiers().toArray(new Annotation[] {}))) {
if (decoratorMatches(decorator.getDelegateType(), decorator.getDelegateQualifiers(), types, Set.of(qualifiers))) {
decorators.add(decorator);
}
}
Expand Down Expand Up @@ -866,12 +877,12 @@ private boolean matches(Set<Type> beanTypes, Set<Annotation> beanQualifiers, Typ
return registeredQualifiers.hasQualifiers(beanQualifiers, qualifiers);
}

private boolean decoratorMatches(Set<Type> beanTypes, Set<Annotation> beanQualifiers, Type delegateType,
Annotation... delegateQualifiers) {
if (!DelegateInjectionPointAssignabilityRules.instance().matches(delegateType, beanTypes)) {
private boolean decoratorMatches(Type delegateType, Set<Annotation> delegateQualifiers, Set<Type> requiredTypes,
Set<Annotation> requiredQualifiers) {
if (!DelegateInjectionPointAssignabilityRules.instance().matches(delegateType, requiredTypes)) {
return false;
}
return registeredQualifiers.hasQualifiers(beanQualifiers, delegateQualifiers);
return registeredQualifiers.hasQualifiers(delegateQualifiers, requiredQualifiers.toArray(new Annotation[0]));
}

static ArcContainerImpl unwrap(ArcContainer container) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ public boolean isQualifier(Class<? extends Annotation> annotationType) {
@Override
public boolean isInterceptorBinding(Class<? extends Annotation> annotationType) {
return annotationType.isAnnotationPresent(InterceptorBinding.class)
|| ArcContainerImpl.instance().getTransitiveInterceptorBindings().containsKey(annotationType);
|| ArcContainerImpl.instance().registeredInterceptorBindings.isRegistered(annotationType);
}

@Override
Expand Down
Loading