Skip to content

Commit

Permalink
Fix mutated metadata being cleared before it was written in Groovy cl…
Browse files Browse the repository at this point in the history
…asses (#6749)

* Fix mutated metadata being cleared before it was written in Groovy classes

* Add test for mutated metadata being used in bean definitions

* Read defaults from compile time information for mutable metadata

* Reuse existing test instead

* Fix test
  • Loading branch information
jameskleeh authored and yawkat committed Jan 21, 2022
1 parent cba5528 commit 7ae026d
Show file tree
Hide file tree
Showing 9 changed files with 79 additions and 9 deletions.
Expand Up @@ -105,6 +105,7 @@ class TypeElementVisitorEnd implements ASTTransformation, CompilationUnitAware {
TypeElementVisitorTransform.loadedVisitors.remove()
TypeElementVisitorTransform.beanDefinitionBuilders.remove()
AstAnnotationUtils.invalidateCache()
AbstractAnnotationMetadataBuilder.clearMutated()
}

@Override
Expand Down
@@ -0,0 +1,31 @@
package io.micronaut.inject.visitor

import io.micronaut.ast.transform.test.AbstractBeanDefinitionSpec

class AnnotationMetadataSpec extends AbstractBeanDefinitionSpec {

void "test mutated metadata from a visitor is available on beans"() {
when:
def definition = buildBeanDefinition('test.TestListener', '''
package test
import io.micronaut.context.annotation.*
import jakarta.inject.Singleton
import io.micronaut.inject.visitor.SomeAnn
@Singleton
class TestListener {
@SomeAnn
@Executable
void receive(String v) {
}
}
''')

then:
noExceptionThrown() //asserts default values are available
definition.findMethod("receive", String).get().hasAnnotation('my.custom.Annotation')
}
}
@@ -0,0 +1,11 @@
package io.micronaut.inject.visitor

import io.micronaut.inject.ast.MethodElement

class MutatingVisitor implements TypeElementVisitor<Object, SomeAnn> {

void visitMethod(MethodElement element, VisitorContext context) {
element.annotate("my.custom.Annotation")
element.annotationMetadata.findAnnotation(SomeAnn).get().getRequiredValue("someValue", String)
}
}
@@ -1,5 +1,5 @@
package io.micronaut.inject.visitor

@interface SomeAnn {

String someValue() default "OK"
}
Expand Up @@ -2,4 +2,5 @@ io.micronaut.inject.visitor.TestInjectVisitor
io.micronaut.inject.visitor.AllElementsVisitor
io.micronaut.inject.visitor.AllClassesVisitor
io.micronaut.inject.visitor.ControllerGetVisitor
io.micronaut.inject.visitor.IntroductionVisitor
io.micronaut.inject.visitor.IntroductionVisitor
io.micronaut.inject.visitor.MutatingVisitor
Expand Up @@ -49,6 +49,7 @@
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.core.util.StringUtils;
import io.micronaut.core.value.OptionalValues;
import io.micronaut.inject.annotation.AbstractAnnotationMetadataBuilder;
import io.micronaut.inject.annotation.AnnotationMetadataHierarchy;
import io.micronaut.inject.annotation.AnnotationMetadataReference;
import io.micronaut.inject.ast.ClassElement;
Expand Down Expand Up @@ -318,6 +319,7 @@ public final boolean process(Set<? extends TypeElement> annotations, RoundEnviro
}
} finally {
AnnotationUtils.invalidateCache();
AbstractAnnotationMetadataBuilder.clearMutated();
JavaAnnotationMetadataBuilder.clearCaches();
}
}
Expand Down
Expand Up @@ -2004,8 +2004,15 @@ public static boolean isMetadataMutated(String declaringType, Object element) {
* Used to clear mutated metadata at the end of a compilation cycle.
*/
@Internal
public static void clearCaches() {
public static void clearMutated() {
MUTATED_ANNOTATION_METADATA.clear();
}

/**
* Used to clear caches at the end of a compilation cycle.
*/
@Internal
public static void clearCaches() {
ANNOTATION_DEFAULTS.clear();
}

Expand Down
Expand Up @@ -982,7 +982,7 @@ <T> Optional<T> getDefaultValue(@NonNull String annotation, @NonNull String memb
ArgumentUtils.requireNonNull("member", member);
ArgumentUtils.requireNonNull("requiredType", requiredType);

Map<String, Object> defaultValues = AnnotationMetadataSupport.getDefaultValues(annotation);
Map<String, Object> defaultValues = getDefaultValues(annotation);
if (defaultValues.containsKey(member)) {
final Object v = defaultValues.get(member);
if (requiredType.isInstance(v)) {
Expand Down Expand Up @@ -1270,11 +1270,11 @@ <T extends Annotation> Optional<AnnotationValue<T>> findAnnotation(@NonNull Stri
if (allAnnotations != null && StringUtils.isNotEmpty(annotation)) {
Map<CharSequence, Object> values = allAnnotations.get(annotation);
if (values != null) {
return Optional.of(new AnnotationValue<>(annotation, values, AnnotationMetadataSupport.getDefaultValues(annotation)));
return Optional.of(new AnnotationValue<>(annotation, values, getDefaultValues(annotation)));
} else if (allStereotypes != null) {
values = allStereotypes.get(annotation);
if (values != null) {
return Optional.of(new AnnotationValue<>(annotation, values, AnnotationMetadataSupport.getDefaultValues(annotation)));
return Optional.of(new AnnotationValue<>(annotation, values, getDefaultValues(annotation)));
}
}
}
Expand All @@ -1289,11 +1289,11 @@ <T extends Annotation> Optional<AnnotationValue<T>> findDeclaredAnnotation(@NonN
if (declaredAnnotations != null && StringUtils.isNotEmpty(annotation)) {
Map<CharSequence, Object> values = declaredAnnotations.get(annotation);
if (values != null) {
return Optional.of(new AnnotationValue<>(annotation, values, AnnotationMetadataSupport.getDefaultValues(annotation)));
return Optional.of(new AnnotationValue<>(annotation, values, getDefaultValues(annotation)));
} else if (declaredStereotypes != null) {
values = declaredStereotypes.get(annotation);
if (values != null) {
return Optional.of(new AnnotationValue<>(annotation, values, AnnotationMetadataSupport.getDefaultValues(annotation)));
return Optional.of(new AnnotationValue<>(annotation, values, getDefaultValues(annotation)));
}
}
}
Expand Down Expand Up @@ -1344,7 +1344,7 @@ <T> Optional<T> getDefaultValue(@NonNull String annotation, @NonNull String memb
ArgumentUtils.requireNonNull("member", member);
ArgumentUtils.requireNonNull("requiredType", requiredType);
// Note this method should never reference the "annotationDefaultValues" field, which is used only at compile time
Map<String, Object> defaultValues = AnnotationMetadataSupport.getDefaultValues(annotation);
Map<String, Object> defaultValues = getDefaultValues(annotation);
if (defaultValues.containsKey(member)) {
return ConversionService.SHARED.convert(defaultValues.get(member), requiredType);
}
Expand Down
Expand Up @@ -20,6 +20,7 @@
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.util.CollectionUtils;
import org.jetbrains.annotations.NotNull;

import java.lang.annotation.Annotation;
import java.lang.annotation.RetentionPolicy;
Expand All @@ -30,6 +31,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
* A mutable various of {@link DefaultAnnotationMetadata} that is used only at build time.
Expand All @@ -38,6 +40,7 @@
* @since 2.4.0
*/
public class MutableAnnotationMetadata extends DefaultAnnotationMetadata {

private boolean hasPropertyExpressions = false;

/**
Expand Down Expand Up @@ -82,6 +85,20 @@ public MutableAnnotationMetadata clone() {
return cloned;
}

@NotNull
@Override
public Map<String, Object> getDefaultValues(@NonNull String annotation) {
Map<String, Object> values = super.getDefaultValues(annotation);
if (values.isEmpty() && annotationDefaultValues != null) {
final Map<CharSequence, Object> compileTimeDefaults = annotationDefaultValues.get(annotation);
if (compileTimeDefaults != null && !compileTimeDefaults.isEmpty()) {
return compileTimeDefaults.entrySet().stream()
.collect(Collectors.toMap(e -> e.getKey().toString(), Map.Entry::getValue));
}
}
return values;
}

@Override
public <A extends Annotation> void removeAnnotationIf(@NonNull Predicate<AnnotationValue<A>> predicate) {
super.removeAnnotationIf(predicate);
Expand Down

0 comments on commit 7ae026d

Please sign in to comment.