Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.micronaut.inject.annotation.AnnotationMetadataSupport Maven / Gradle / Ivy
/*
* Copyright 2017-2020 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.micronaut.inject.annotation;
import io.micronaut.context.annotation.AliasFor;
import io.micronaut.context.annotation.Aliases;
import io.micronaut.context.annotation.Any;
import io.micronaut.context.annotation.Bean;
import io.micronaut.context.annotation.Configuration;
import io.micronaut.context.annotation.ConfigurationBuilder;
import io.micronaut.context.annotation.ConfigurationProperties;
import io.micronaut.context.annotation.ConfigurationReader;
import io.micronaut.context.annotation.Context;
import io.micronaut.context.annotation.DefaultImplementation;
import io.micronaut.context.annotation.DefaultScope;
import io.micronaut.context.annotation.EachBean;
import io.micronaut.context.annotation.EachProperty;
import io.micronaut.context.annotation.Executable;
import io.micronaut.context.annotation.Factory;
import io.micronaut.context.annotation.Parallel;
import io.micronaut.context.annotation.Parameter;
import io.micronaut.context.annotation.Primary;
import io.micronaut.context.annotation.Property;
import io.micronaut.context.annotation.PropertySource;
import io.micronaut.context.annotation.Prototype;
import io.micronaut.context.annotation.Provided;
import io.micronaut.context.annotation.Replaces;
import io.micronaut.context.annotation.Requirements;
import io.micronaut.context.annotation.Requires;
import io.micronaut.context.annotation.Secondary;
import io.micronaut.context.annotation.Type;
import io.micronaut.context.annotation.Value;
import io.micronaut.context.condition.TrueCondition;
import io.micronaut.core.annotation.AccessorsStyle;
import io.micronaut.core.annotation.AnnotationClassValue;
import io.micronaut.core.annotation.AnnotationDefaultValuesProvider;
import io.micronaut.core.annotation.AnnotationUtil;
import io.micronaut.core.annotation.AnnotationValue;
import io.micronaut.core.annotation.AnnotationValueProvider;
import io.micronaut.core.annotation.Indexed;
import io.micronaut.core.annotation.Indexes;
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.annotation.Introspected;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.annotation.Order;
import io.micronaut.core.annotation.ReflectionConfig;
import io.micronaut.core.annotation.UsedByGeneratedCode;
import io.micronaut.core.bind.annotation.Bindable;
import io.micronaut.core.convert.format.Format;
import io.micronaut.core.convert.format.MapFormat;
import io.micronaut.core.convert.format.ReadableBytes;
import io.micronaut.core.reflect.ClassUtils;
import io.micronaut.core.reflect.InstantiationUtils;
import io.micronaut.core.reflect.ReflectionUtils;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.core.util.StringUtils;
import jakarta.annotation.Nonnull;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.inject.Qualifier;
import jakarta.inject.Scope;
import jakarta.inject.Singleton;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.AbstractMap;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import static io.micronaut.core.annotation.AnnotationClassValue.ZERO_ANNOTATION_CLASS_VALUES;
import static io.micronaut.core.annotation.AnnotationUtil.ZERO_ANNOTATION_VALUES;
import static io.micronaut.core.util.StringUtils.EMPTY_STRING_ARRAY;
/**
* Support method for {@link io.micronaut.core.annotation.AnnotationMetadata}.
*
* @author Graeme Rocher
* @since 1.0
*/
@Internal
public final class AnnotationMetadataSupport {
private static final Map> CORE_ANNOTATION_DEFAULTS;
private static final Map> ANNOTATION_DEFAULTS = new ConcurrentHashMap<>(20);
private static final Map REPEATABLE_ANNOTATIONS_CONTAINERS = new ConcurrentHashMap<>(20);
private static final Map CORE_REPEATABLE_ANNOTATIONS_CONTAINERS;
private static final Map, Optional>> ANNOTATION_PROXY_CACHE = new ConcurrentHashMap<>(20);
private static final Map> ANNOTATION_TYPES = new ConcurrentHashMap<>(20);
/**
* The annotation default values provider.
* @since 4.3.0
*/
public static final AnnotationDefaultValuesProvider ANNOTATION_DEFAULT_VALUES_PROVIDER = new AnnotationDefaultValuesProvider() {
@Override
public Map provide(String annotationName) {
return AnnotationMetadataSupport.getDefaultValues(annotationName);
}
};
static {
// some common ones for startup optimization
Arrays.asList(
Any.class,
jakarta.annotation.Nullable.class,
Nonnull.class,
ReadableBytes.class,
Format.class,
Indexed.class,
Bindable.class,
DefaultScope.class,
Internal.class,
DefaultImplementation.class,
Nullable.class,
NonNull.class,
PreDestroy.class,
PostConstruct.class,
Named.class,
Singleton.class,
Inject.class,
Qualifier.class,
Scope.class,
Prototype.class,
Executable.class,
Bean.class,
Primary.class,
Value.class,
Property.class,
Provided.class,
Requires.class,
Secondary.class,
Type.class,
Context.class,
EachBean.class,
EachProperty.class,
Configuration.class,
ConfigurationProperties.class,
ConfigurationBuilder.class,
Introspected.class,
Parameter.class,
Requirements.class,
Factory.class).forEach(ann ->
ANNOTATION_TYPES.put(ann.getName(), ann)
);
Map> coreAnnotationsDefaults = new HashMap<>(100);
coreAnnotationsDefaults.put(
Deprecated.class.getName(),
Map.of("forRemoval", false)
);
coreAnnotationsDefaults.put(
Order.class.getName(),
Map.of("value", 0)
);
coreAnnotationsDefaults.put(
Executable.class.getName(),
Map.of("processOnStartup", false)
);
coreAnnotationsDefaults.put(
ConfigurationProperties.class.getName(),
Map.of("cliPrefix", EMPTY_STRING_ARRAY, "excludes", EMPTY_STRING_ARRAY, "includes", EMPTY_STRING_ARRAY)
);
coreAnnotationsDefaults.put(
EachProperty.class.getName(),
Map.of("excludes", EMPTY_STRING_ARRAY, "includes", EMPTY_STRING_ARRAY, "list", false)
);
coreAnnotationsDefaults.put(
ConfigurationReader.class.getName(),
Map.of("excludes", EMPTY_STRING_ARRAY, "includes", EMPTY_STRING_ARRAY)
);
coreAnnotationsDefaults.put(
Bean.class.getName(),
Map.of("typed", ZERO_ANNOTATION_CLASS_VALUES)
);
coreAnnotationsDefaults.put(
Requires.class.getName(),
Map.ofEntries(Map.entry("beans", ZERO_ANNOTATION_CLASS_VALUES), Map.entry("classes", ZERO_ANNOTATION_CLASS_VALUES), Map.entry("condition", TrueCondition.class), Map.entry("entities", ZERO_ANNOTATION_CLASS_VALUES), Map.entry("env", EMPTY_STRING_ARRAY), Map.entry("missing", ZERO_ANNOTATION_CLASS_VALUES), Map.entry("missingBeans", ZERO_ANNOTATION_CLASS_VALUES), Map.entry("missingClasses", EMPTY_STRING_ARRAY), Map.entry("missingConfigurations", EMPTY_STRING_ARRAY), Map.entry("notEnv", EMPTY_STRING_ARRAY), Map.entry("notOs", EMPTY_STRING_ARRAY), Map.entry("os", EMPTY_STRING_ARRAY), Map.entry("resources", EMPTY_STRING_ARRAY), Map.entry("sdk", "MICRONAUT"))
);
coreAnnotationsDefaults.put(
Replaces.class.getName(),
Map.of("qualifier", Annotation.class)
);
coreAnnotationsDefaults.put(
Introspected.IntrospectionBuilder.class.getName(),
Map.of("accessorStyle", new AnnotationValue("io.micronaut.core.annotation.AccessorsStyle", Map.of("writePrefixes", new String[]{""}), AnnotationMetadataSupport.ANNOTATION_DEFAULT_VALUES_PROVIDER), "creatorMethod", "build")
);
coreAnnotationsDefaults.put(
Introspected.class.getName(),
Map.ofEntries(Map.entry("accessKind", new String[]{"METHOD"}), Map.entry("annotationMetadata", true), Map.entry("builder", new AnnotationValue("io.micronaut.core.annotation.Introspected$IntrospectionBuilder", Map.of(), AnnotationMetadataSupport.ANNOTATION_DEFAULT_VALUES_PROVIDER)), Map.entry("classNames", EMPTY_STRING_ARRAY), Map.entry("classes", ZERO_ANNOTATION_CLASS_VALUES), Map.entry("excludedAnnotations", ZERO_ANNOTATION_CLASS_VALUES), Map.entry("excludes", EMPTY_STRING_ARRAY), Map.entry("includedAnnotations", ZERO_ANNOTATION_CLASS_VALUES), Map.entry("includes", EMPTY_STRING_ARRAY), Map.entry("indexed", ZERO_ANNOTATION_VALUES), Map.entry("packages", EMPTY_STRING_ARRAY), Map.entry("visibility", new String[]{"DEFAULT"}), Map.entry("withPrefix", "with"))
);
coreAnnotationsDefaults.put(
MapFormat.class.getName(),
Map.of("keyFormat", "HYPHENATED", "transformation", "NESTED")
);
coreAnnotationsDefaults.put(
Parallel.class.getName(),
Map.of("shutdownOnError", true)
);
coreAnnotationsDefaults.put(
"io.micronaut.aop.constructor.TestConstructorAnn",
Map.of()
);
coreAnnotationsDefaults.put(
"io.micronaut.aop.AroundConstruct",
Map.of()
);
coreAnnotationsDefaults.put(
"io.micronaut.aop.InterceptorBinding",
Map.of("bindMembers", false, "kind", "AROUND", "value", Annotation.class)
);
coreAnnotationsDefaults.put(
"io.micronaut.aop.InterceptorBean",
Map.of()
);
coreAnnotationsDefaults.put(
"io.micronaut.aop.Around",
Map.of("cacheableLazyTarget", false, "hotswap", false, "lazy", false, "proxyTarget", false, "proxyTargetMode", "ERROR")
);
coreAnnotationsDefaults.put(
"io.micronaut.aop.Introduction",
Map.of("interfaces", ZERO_ANNOTATION_CLASS_VALUES)
);
coreAnnotationsDefaults.put(
"io.micronaut.aop.Adapter",
Map.of()
);
coreAnnotationsDefaults.put(
"io.micronaut.validation.annotation.ValidatedElement",
Map.of()
);
coreAnnotationsDefaults.put(
AccessorsStyle.class.getName(),
Map.of("readPrefixes", new String[]{"get"}, "writePrefixes", new String[]{"set"})
);
CORE_ANNOTATION_DEFAULTS = Collections.unmodifiableMap(coreAnnotationsDefaults);
ANNOTATION_DEFAULTS.putAll(CORE_ANNOTATION_DEFAULTS);
Map coreRepeatableAnnotationsContainers = new LinkedHashMap<>();
for (Map.Entry, Class extends Annotation>> e : getCoreRepeatableAnnotations()) {
coreRepeatableAnnotationsContainers.put(e.getKey().getName(), e.getValue().getName());
}
coreRepeatableAnnotationsContainers.put("io.micronaut.aop.InterceptorBinding", "io.micronaut.aop.InterceptorBindingDefinitions");
CORE_REPEATABLE_ANNOTATIONS_CONTAINERS = Collections.unmodifiableMap(coreRepeatableAnnotationsContainers);
REPEATABLE_ANNOTATIONS_CONTAINERS.putAll(coreRepeatableAnnotationsContainers);
}
/**
* @return core repeatable annotations
*/
@Internal
public static List, Class extends Annotation>>> getCoreRepeatableAnnotations() {
return Arrays.asList(
new AbstractMap.SimpleEntry<>(Indexed.class, Indexes.class),
new AbstractMap.SimpleEntry<>(Requires.class, Requirements.class),
new AbstractMap.SimpleEntry<>(AliasFor.class, Aliases.class),
new AbstractMap.SimpleEntry<>(Property.class, PropertySource.class),
new AbstractMap.SimpleEntry<>(ReflectionConfig.class, ReflectionConfig.ReflectionConfigList.class)
);
}
/**
* @return The core repeatable annotations.
* @since 4.3.0
*/
@Internal
public static Map getCoreRepeatableAnnotationsContainers() {
return CORE_REPEATABLE_ANNOTATIONS_CONTAINERS;
}
/**
* @return The core annotation defaults.
* @since 4.3.0
*/
@Internal
public static Map> getCoreAnnotationDefaults() {
return CORE_ANNOTATION_DEFAULTS;
}
/**
* @param annotation The annotation
* @return The default values for the annotation
*/
@UsedByGeneratedCode
@NonNull
public static Map getDefaultValues(String annotation) {
return ANNOTATION_DEFAULTS.getOrDefault(annotation, Collections.emptyMap());
}
/**
* @param annotation The annotation
* @return The default values for the annotation
*/
@Nullable
public static Map getDefaultValuesOrNull(String annotation) {
return ANNOTATION_DEFAULTS.get(annotation);
}
/**
* @param annotation The annotation
* @return The repeatable annotation container.
*/
@Internal
public static String getRepeatableAnnotation(String annotation) {
return REPEATABLE_ANNOTATIONS_CONTAINERS.get(annotation);
}
/**
* Gets a registered annotation type.
*
* @param name The name of the annotation type
* @return The annotation
*/
static Optional> getAnnotationType(String name) {
return getAnnotationType(name, AnnotationMetadataSupport.class.getClassLoader());
}
/**
* Gets a registered annotation type.
*
* @param name The name of the annotation type
* @param classLoader The classloader to retrieve the type
* @return The annotation
*/
static Optional> getAnnotationType(String name, ClassLoader classLoader) {
final Class extends Annotation> type = ANNOTATION_TYPES.get(name);
if (type != null) {
return Optional.of(type);
} else {
// last resort, try dynamic load, shouldn't normally happen.
@SuppressWarnings("unchecked") final Class extends Annotation> aClass =
(Class extends Annotation>) ClassUtils.forName(name, classLoader).orElse(null);
if (aClass != null && Annotation.class.isAssignableFrom(aClass)) {
ANNOTATION_TYPES.put(name, aClass);
return Optional.of(aClass);
}
return Optional.empty();
}
}
/**
* Gets a registered annotation type.
*
* @param name The name of the annotation type
* @return The annotation
*/
static Optional> getRegisteredAnnotationType(String name) {
final Class extends Annotation> type = ANNOTATION_TYPES.get(name);
if (type != null) {
return Optional.of(type);
}
return Optional.empty();
}
/**
* @param annotation The annotation
* @return The default values for the annotation
*/
@SuppressWarnings("unchecked")
static Map getDefaultValues(Class extends Annotation> annotation) {
return getDefaultValues(annotation.getName());
}
/**
* Whether default values for the given annotation are present.
*
* @param annotation The annotation
* @return True if they are
*/
static boolean hasDefaultValues(String annotation) {
return ANNOTATION_DEFAULTS.containsKey(annotation);
}
/**
* Registers default values for the given annotation and values.
*
* @param annotation The annotation
* @param defaultValues The default values
*/
static void registerDefaultValues(String annotation, Map defaultValues) {
if (StringUtils.isNotEmpty(annotation) && CollectionUtils.isNotEmpty(defaultValues)) {
ANNOTATION_DEFAULTS.put(annotation, defaultValues);
}
}
/**
* Registers default values for the given annotation and values.
*
* @param annotation The annotation
* @param defaultValues The default values
*/
static void registerDefaultValues(AnnotationClassValue> annotation, Map defaultValues) {
if (defaultValues != null) {
registerDefaultValues(annotation.getName(), defaultValues);
}
registerAnnotationType(annotation);
}
/**
* Registers an annotation type.
*
* @param annotationClassValue the annotation class value
*/
@SuppressWarnings("unchecked")
static void registerAnnotationType(AnnotationClassValue> annotationClassValue) {
final String name = annotationClassValue.getName();
if (!ANNOTATION_TYPES.containsKey(name)) {
Class> aClass = annotationClassValue.getType().orElse(null);
if (aClass != null && Annotation.class.isAssignableFrom(aClass)) {
ANNOTATION_TYPES.put(name, (Class extends Annotation>) aClass);
}
}
}
/**
* Registers repeatable annotation containers.
* @MyRepeatable -> @MyRepeatableContainer
*
* @param repeatableAnnotations the repeatable annotations
*/
@Internal
static void registerRepeatableAnnotations(Map repeatableAnnotations) {
REPEATABLE_ANNOTATIONS_CONTAINERS.putAll(repeatableAnnotations);
}
/**
* Registers repeatable annotation containers.
*
* @param repeatable the repeatable annotations
* @param repeatableContainer the repeatable annotation container
* @MyRepeatable -> @MyRepeatableContainer
* @since 4.0.0
*/
@Internal
static void registerRepeatableAnnotation(@NonNull String repeatable, @NonNull String repeatableContainer) {
REPEATABLE_ANNOTATIONS_CONTAINERS.put(repeatable, repeatableContainer);
}
/**
* @param annotation The annotation
* @return The proxy class
*/
@SuppressWarnings("unchecked")
static Optional> getProxyClass(Class extends Annotation> annotation) {
return ANNOTATION_PROXY_CACHE.computeIfAbsent(annotation, aClass -> {
Class proxyClass = Proxy.getProxyClass(annotation.getClassLoader(), annotation, AnnotationValueProvider.class);
return ReflectionUtils.findConstructor(proxyClass, InvocationHandler.class);
});
}
/**
* Builds the annotation based on the annotation value.
*
* @param annotationClass The annotation class
* @param annotationValue The annotation value
* @param The type
* @return The annotation
*/
@Internal
public static T buildAnnotation(Class annotationClass, @Nullable AnnotationValue annotationValue) {
Optional> proxyClass = getProxyClass(annotationClass);
if (proxyClass.isPresent()) {
Map values = new HashMap<>(getDefaultValues(annotationClass));
if (annotationValue != null) {
annotationValue.getValues().forEach((key, o) -> values.put(key.toString(), o));
}
int hashCode = AnnotationUtil.calculateHashCode(values);
Optional> instantiated = InstantiationUtils.tryInstantiate(proxyClass.get(), new AnnotationProxyHandler<>(hashCode, annotationClass, annotationValue));
if (instantiated.isPresent()) {
return (T) instantiated.get();
}
}
throw new AnnotationMetadataException("Failed to build annotation for type: " + annotationClass.getName());
}
/**
* Annotation proxy handler.
*
* @param The annotation type
*/
private static class AnnotationProxyHandler implements InvocationHandler, AnnotationValueProvider {
private final int hashCode;
private final Class annotationClass;
private final AnnotationValue annotationValue;
AnnotationProxyHandler(int hashCode, Class annotationClass, @Nullable AnnotationValue annotationValue) {
this.hashCode = hashCode;
this.annotationClass = annotationClass;
this.annotationValue = annotationValue;
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!annotationClass.isInstance(obj)) {
return false;
}
Annotation other = annotationClass.cast(obj);
final AnnotationValue> otherValues = getAnnotationValues(other);
if (this.annotationValue == null && otherValues == null) {
return true;
} else if (this.annotationValue == null || otherValues == null) {
return false;
} else {
return annotationValue.equals(otherValues);
}
}
private AnnotationValue> getAnnotationValues(Annotation other) {
if (other instanceof AnnotationProxyHandler> handler) {
return handler.annotationValue;
}
return null;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
String name = method.getName();
if ((args == null || args.length == 0) && "hashCode".equals(name)) {
return hashCode;
} else if ((args != null && args.length == 1) && "equals".equals(name)) {
return equals(args[0]);
} else if ("toString".equals(name)) {
return annotationValue.toString();
} else if ("annotationType".equals(name)) {
return annotationClass;
} else if (method.getReturnType() == AnnotationValue.class) {
return annotationValue;
} else if (annotationValue != null && annotationValue.contains(name)) {
return annotationValue.getRequiredValue(name, method.getReturnType());
}
return method.getDefaultValue();
}
@NonNull
@Override
public AnnotationValue annotationValue() {
if (annotationValue != null) {
return this.annotationValue;
} else {
return new AnnotationValue<>(annotationClass.getName());
}
}
}
}