Skip to content
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 @@ -18,6 +18,9 @@

import static org.junit.Assert.assertNull;

import java.util.HashSet;
import java.util.Set;

import jakarta.enterprise.event.Observes;
import jakarta.enterprise.inject.Stereotype;
import jakarta.enterprise.inject.spi.AnnotatedType;
Expand All @@ -32,6 +35,10 @@ public class VerifyingExtension implements Extension {

private AnnotatedType<MyBeanMeta> myBeanMetaType;

private final Set<Class<?>> markedTypes = new HashSet<>();

private final Set<Class<?>> inheritedMarkedTypes = new HashSet<>();

void processMyBean(@Observes @WithAnnotations(Named.class) ProcessAnnotatedType<MyBean> event) {
assertNull(myBeanType);
this.myBeanType = event.getAnnotatedType();
Expand All @@ -42,6 +49,15 @@ void processMyBeanMeta(@Observes @WithAnnotations(Stereotype.class) ProcessAnnot
this.myBeanMetaType = event.getAnnotatedType();
}

void processMarkedType(@Observes @WithAnnotations(WithAnnotationsTest.Marker.class) ProcessAnnotatedType<?> event) {
markedTypes.add(event.getAnnotatedType().getJavaClass());
}

void processInheritedMarkedType(
@Observes @WithAnnotations(WithAnnotationsTest.InheritedMarker.class) ProcessAnnotatedType<?> event) {
inheritedMarkedTypes.add(event.getAnnotatedType().getJavaClass());
}

AnnotatedType<MyBean> getMyBeanType() {
return myBeanType;
}
Expand All @@ -50,4 +66,12 @@ AnnotatedType<MyBeanMeta> getMyBeanMetaType() {
return myBeanMetaType;
}

Set<Class<?>> getMarkedTypes() {
return markedTypes;
}

Set<Class<?>> getInheritedMarkedTypes() {
return inheritedMarkedTypes;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Set;

import jakarta.enterprise.context.Dependent;
import jakarta.enterprise.inject.spi.Extension;

import org.jboss.arquillian.container.se.api.ClassPath;
Expand All @@ -41,6 +47,7 @@ public static Archive<?> getDeployment() {
return ClassPath.builder()
.add(ShrinkWrap.create(BeanArchive.class, Utils.getDeploymentNameAsHash(WithAnnotationsTest.class))
.addPackage(WithAnnotationsTest.class.getPackage())
.addClasses(WithAnnotationsTest.class.getDeclaredClasses())
.addAsServiceProvider(Extension.class, VerifyingExtension.class))
.build();
}
Expand All @@ -59,4 +66,32 @@ public void testWithAnnotationsOnDefaultMethod() {
}

}

@Test
public void testWithAnnotationsRespectsClassAnnotationInheritance() {
try (WeldContainer container = new Weld().scanClasspathEntries().initialize()) {
VerifyingExtension extension = container.getBeanManager().getExtension(VerifyingExtension.class);
assertEquals(Set.of(Base.class), extension.getMarkedTypes());
assertEquals(Set.of(Base.class, Child.class), extension.getInheritedMarkedTypes());
}
}

@Retention(RetentionPolicy.RUNTIME)
public @interface Marker {
}

@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface InheritedMarker {
}

@Dependent
@Marker
@InheritedMarker
public static class Base {
}

@Dependent
public static class Child extends Base {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import jakarta.enterprise.event.Observes;
import jakarta.enterprise.event.ObservesAsync;
Expand Down Expand Up @@ -290,45 +292,52 @@ protected EnhancedAnnotatedTypeImpl(SlimAnnotatedType<T> annotatedType,
SetMultimap<Class<? extends Annotation>, Annotation> declaredMetaAnnotationMap = SetMultimap.newSetMultimap();
processMetaAnnotations(declaredMetaAnnotationMap, declaredAnnotationMap.values(), classTransformer, true);
this.declaredMetaAnnotationMap = Multimaps.unmodifiableMultimap(declaredMetaAnnotationMap);
this.overriddenMethods = getOverriddenMethods(this, methodsTemp);
Map<Class<?>, List<EnhancedAnnotatedMethod<?, ? super T>>> methodsByDeclaringClass = groupMethodsByDeclaringClass(
methodsTemp);
this.overriddenMethods = getOverriddenMethods(this, methodsByDeclaringClass);

// WELD-1548 remove all overriden methods except for those which are overriden by a bridge method
methodsTemp.removeAll(getOverriddenMethods(this, methodsTemp, true));
methodsTemp.removeAll(getOverriddenMethods(this, methodsByDeclaringClass, true));

this.methods = methodsTemp;
this.annotatedMethods = buildAnnotatedMethodMultimap(this.methods);
this.annotatedMethodsByAnnotatedParameters = buildAnnotatedParameterMethodMultimap(this.methods);
}

protected Set<EnhancedAnnotatedMethod<?, ? super T>> getOverriddenMethods(EnhancedAnnotatedType<T> annotatedType,
protected Map<Class<?>, List<EnhancedAnnotatedMethod<?, ? super T>>> groupMethodsByDeclaringClass(
Set<EnhancedAnnotatedMethod<?, ? super T>> methods) {
return getOverriddenMethods(annotatedType, methods, false);
return methods.stream().collect(Collectors.groupingBy(method -> method.getJavaMember().getDeclaringClass()));
}

protected Set<EnhancedAnnotatedMethod<?, ? super T>> getOverriddenMethods(EnhancedAnnotatedType<T> annotatedType,
Map<Class<?>, List<EnhancedAnnotatedMethod<?, ? super T>>> methodsByDeclaringClass) {
return getOverriddenMethods(annotatedType, methodsByDeclaringClass, false);
}

/**
*
* @param annotatedType
* @param methods
* @param methodsByDeclaringClass
* @param skipOverridingBridgeMethods If set to <code>true</code> the returning set will not contain methods overriden by a
* bridge method
* @return the set of overriden methods
*/
protected Set<EnhancedAnnotatedMethod<?, ? super T>> getOverriddenMethods(EnhancedAnnotatedType<T> annotatedType,
Set<EnhancedAnnotatedMethod<?, ? super T>> methods, boolean skipOverridingBridgeMethods) {
Map<Class<?>, List<EnhancedAnnotatedMethod<?, ? super T>>> methodsByDeclaringClass,
boolean skipOverridingBridgeMethods) {
Set<EnhancedAnnotatedMethod<?, ? super T>> overriddenMethods = new HashSet<EnhancedAnnotatedMethod<?, ? super T>>();
Multimap<MethodSignature, Package> seenMethods = SetMultimap.newSetMultimap();
for (Class<? super T> clazz = annotatedType.getJavaClass(); clazz != null
&& clazz != Object.class; clazz = clazz.getSuperclass()) {
for (EnhancedAnnotatedMethod<?, ? super T> method : methods) {
if (method.getJavaMember().getDeclaringClass().equals(clazz)) {
if (skipOverridingBridgeMethods && method.getJavaMember().isBridge()) {
continue;
}
if (isOverridden(method, seenMethods)) {
overriddenMethods.add(method);
}
seenMethods.put(method.getSignature(), method.getPackage());
for (EnhancedAnnotatedMethod<?, ? super T> method : methodsByDeclaringClass.getOrDefault(clazz,
Collections.emptyList())) {
if (skipOverridingBridgeMethods && method.getJavaMember().isBridge()) {
continue;
}
if (isOverridden(method, seenMethods)) {
overriddenMethods.add(method);
}
seenMethods.put(method.getSignature(), method.getPackage());
}
}
return immutableSetView(overriddenMethods);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

Expand Down Expand Up @@ -87,9 +88,16 @@ protected EnhancedAnnotationImpl(SlimAnnotatedType<T> annotatedType,
}
}

@Override
protected Map<Class<?>, List<EnhancedAnnotatedMethod<?, ? super T>>> groupMethodsByDeclaringClass(
Set<EnhancedAnnotatedMethod<?, ? super T>> methods) {
return Collections.emptyMap();
}

@Override
protected Set<EnhancedAnnotatedMethod<?, ? super T>> getOverriddenMethods(EnhancedAnnotatedType<T> annotatedType,
Set<EnhancedAnnotatedMethod<?, ? super T>> methods, boolean skipOverridingBridgeMethods) {
Map<Class<?>, List<EnhancedAnnotatedMethod<?, ? super T>>> methodsByDeclaringClass,
boolean skipOverridingBridgeMethods) {
return Collections.emptySet();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,13 @@
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.jboss.weld.annotated.slim.backed.BackedAnnotatedType;
import org.jboss.weld.bootstrap.api.Service;
import org.jboss.weld.bootstrap.api.helpers.AbstractBootstrapService;
import org.jboss.weld.resources.ReflectionCache;
import org.jboss.weld.util.reflection.Reflections;

Expand All @@ -34,10 +38,16 @@
* @author Jozef Hartinger
*
*/
public class RequiredAnnotationDiscovery implements Service {
public class RequiredAnnotationDiscovery extends AbstractBootstrapService {

private final ReflectionCache cache;

/**
* Complete annotation sets from searches that reached the end without a match. Successful searches
* short-circuit without publishing a partial set.
*/
private final ConcurrentMap<Class<?>, Set<Class<? extends Annotation>>> matchingAnnotations = new ConcurrentHashMap<>();

public RequiredAnnotationDiscovery(ReflectionCache cache) {
this.cache = cache;
}
Expand Down Expand Up @@ -81,102 +91,103 @@ public RequiredAnnotationDiscovery(ReflectionCache cache) {
* @return
*/
public boolean containsAnnotation(BackedAnnotatedType<?> annotatedType, Class<? extends Annotation> requiredAnnotation) {
Set<Class<? extends Annotation>> cached = matchingAnnotations.get(annotatedType.getJavaClass());
if (cached != null) {
return cached.contains(requiredAnnotation);
}
Set<Class<? extends Annotation>> found = new HashSet<>();
// class level annotations
if (containsAnnotation(annotatedType.getAnnotations(), requiredAnnotation, true)) {
if (containsAnnotations(annotatedType.getAnnotations(), requiredAnnotation, found)) {
return true;
}
for (Class<?> clazz = annotatedType.getJavaClass(); clazz != null
&& clazz != Object.class; clazz = clazz.getSuperclass()) {
// fields
for (Field field : clazz.getDeclaredFields()) {
if (containsAnnotations(cache.getAnnotations(field), requiredAnnotation)) {
if (containsAnnotations(cache.getAnnotations(field), requiredAnnotation, found)) {
return true;
}
}
// constructors
for (Constructor<?> constructor : clazz.getDeclaredConstructors()) {
if (containsAnnotations(cache.getAnnotations(constructor), requiredAnnotation)) {
if (containsAnnotations(cache.getAnnotations(constructor), requiredAnnotation, found)
|| containsParameterAnnotations(constructor.getParameterAnnotations(), requiredAnnotation, found)) {
return true;
}
for (Annotation[] parameterAnnotations : constructor.getParameterAnnotations()) {
if (containsAnnotations(parameterAnnotations, requiredAnnotation)) {
return true;
}
}
}
// methods
for (Method method : clazz.getDeclaredMethods()) {
if (containsAnnotations(cache.getAnnotations(method), requiredAnnotation)) {
if (containsAnnotations(cache.getAnnotations(method), requiredAnnotation, found)
|| containsParameterAnnotations(method.getParameterAnnotations(), requiredAnnotation, found)) {
return true;
}
for (Annotation[] parameterAnnotations : method.getParameterAnnotations()) {
if (containsAnnotations(parameterAnnotations, requiredAnnotation)) {
return true;
}
}
}
}

// Also check default methods on interfaces
for (Class<?> interfaceClazz : Reflections.getInterfaceClosure(annotatedType.getJavaClass())) {
for (Method method : interfaceClazz.getDeclaredMethods()) {
if (Reflections.isDefault(method)) {
if (containsAnnotations(cache.getAnnotations(method), requiredAnnotation)) {
if (containsAnnotations(cache.getAnnotations(method), requiredAnnotation, found)
|| containsParameterAnnotations(method.getParameterAnnotations(), requiredAnnotation, found)) {
return true;
}
for (Annotation[] parameterAnnotations : method.getParameterAnnotations()) {
if (containsAnnotations(parameterAnnotations, requiredAnnotation)) {
return true;
}
}
}
}
}
matchingAnnotations.putIfAbsent(annotatedType.getJavaClass(), found);
return false;
}

private boolean containsAnnotations(Annotation[] annotations, Class<? extends Annotation> requiredAnnotation) {
return containsAnnotation(annotations, requiredAnnotation, true);
private boolean containsParameterAnnotations(Annotation[][] parameterAnnotations,
Class<? extends Annotation> requiredAnnotation, Set<Class<? extends Annotation>> found) {
for (Annotation[] annotations : parameterAnnotations) {
if (containsAnnotations(annotations, requiredAnnotation, found)) {
return true;
}
}
return false;
}

private boolean containsAnnotation(Annotation[] annotations, Class<? extends Annotation> requiredAnnotation,
boolean checkMetaAnnotations) {
private boolean containsAnnotations(Annotation[] annotations, Class<? extends Annotation> requiredAnnotation,
Set<Class<? extends Annotation>> found) {
for (Annotation annotation : annotations) {
if (containsAnnotation(annotation, requiredAnnotation, checkMetaAnnotations)) {
if (containsAnnotation(annotation, requiredAnnotation, found)) {
return true;
}
}
return false;
}

private boolean containsAnnotations(Iterable<? extends Annotation> annotations,
Class<? extends Annotation> requiredAnnotation) {
return containsAnnotation(annotations, requiredAnnotation, true);
}

private boolean containsAnnotation(Iterable<? extends Annotation> annotations,
Class<? extends Annotation> requiredAnnotation, boolean checkMetaAnnotations) {
Class<? extends Annotation> requiredAnnotation,
Set<Class<? extends Annotation>> found) {
for (Annotation annotation : annotations) {
if (containsAnnotation(annotation, requiredAnnotation, checkMetaAnnotations)) {
if (containsAnnotation(annotation, requiredAnnotation, found)) {
return true;
}
}
return false;
}

private boolean containsAnnotation(Annotation annotation, Class<? extends Annotation> requiredAnnotation,
boolean checkMetaAnnotations) {
Set<Class<? extends Annotation>> found) {
Class<? extends Annotation> annotationType = annotation.annotationType();
if (requiredAnnotation.equals(annotationType)) {
return true;
}
if (checkMetaAnnotations && containsAnnotation(cache.getAnnotations(annotationType), requiredAnnotation, false)) {
return true;
found.add(annotationType);
for (Annotation metaAnnotation : cache.getAnnotations(annotationType)) {
if (requiredAnnotation.equals(metaAnnotation.annotationType())) {
return true;
}
found.add(metaAnnotation.annotationType());
}
return false;
}

@Override
public void cleanup() {
public void cleanupAfterBoot() {
matchingAnnotations.clear();
}
}
Loading
Loading