diff --git a/benchmarks/build.gradle b/benchmarks/build.gradle new file mode 100644 index 00000000000..d3964bcd2cd --- /dev/null +++ b/benchmarks/build.gradle @@ -0,0 +1,21 @@ +plugins { + id "me.champeau.gradle.jmh" version "0.4.8" +} + +dependencies { + annotationProcessor project(":inject-java") + annotationProcessor project(":validation") + compile project(":inject") + compile project(":validation") + compile project(":runtime") + + + jmh 'org.openjdk.jmh:jmh-core:1.21' + jmh 'org.openjdk.jmh:jmh-generator-annprocess:1.21' +} +jmh { + duplicateClassesStrategy = 'warn' + warmupIterations = 2 + iterations = 4 + fork = 1 +} \ No newline at end of file diff --git a/benchmarks/src/jmh/java/io/micronaut/core/annotation/AnnotationValueBenchmark.java b/benchmarks/src/jmh/java/io/micronaut/core/annotation/AnnotationValueBenchmark.java new file mode 100644 index 00000000000..611935694ea --- /dev/null +++ b/benchmarks/src/jmh/java/io/micronaut/core/annotation/AnnotationValueBenchmark.java @@ -0,0 +1,4 @@ +package io.micronaut.core.annotation; + +public class AnnotationValueBenchmark { +} diff --git a/benchmarks/src/jmh/java/io/micronaut/core/convert/ConversionServiceBenchmark.java b/benchmarks/src/jmh/java/io/micronaut/core/convert/ConversionServiceBenchmark.java new file mode 100644 index 00000000000..928d79d86cc --- /dev/null +++ b/benchmarks/src/jmh/java/io/micronaut/core/convert/ConversionServiceBenchmark.java @@ -0,0 +1,45 @@ +package io.micronaut.core.convert; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.net.URI; + +@State(Scope.Benchmark) +public class ConversionServiceBenchmark { + + ConversionService conversionService; + + @Setup + public void prepare() { + conversionService = ConversionService.SHARED; + } + + @Benchmark + public void convertCacheHit() { + conversionService.convert("10", Integer.class); + } + + @Benchmark + public void convertCacheMiss() { + conversionService.convert(URI.create("http://test.com"), Integer.class); + } + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(".*" + ConversionServiceBenchmark.class.getSimpleName() + ".*") + .warmupIterations(3) + .measurementIterations(5) + .forks(1) +// .jvmArgs("-agentpath:/Applications/YourKit-Java-Profiler-2018.04.app/Contents/Resources/bin/mac/libyjpagent.jnilib") + .build(); + + new Runner(opt).run(); + } +} diff --git a/build.gradle b/build.gradle index ddf9f129835..ef4df3f3431 100644 --- a/build.gradle +++ b/build.gradle @@ -547,7 +547,8 @@ subprojects { Project subproject -> if ( !subproject.name.startsWith('test-') && - !subproject.toString().contains('build-projects') + !subproject.toString().contains('build-projects') && + !subproject.toString().contains('benchmarks') ) { apply from: "${rootProject.rootDir}/gradle/publishing.gradle" diff --git a/core/src/main/java/io/micronaut/core/annotation/AnnotationMetadata.java b/core/src/main/java/io/micronaut/core/annotation/AnnotationMetadata.java index f65932fc45a..5ddfdfbee22 100644 --- a/core/src/main/java/io/micronaut/core/annotation/AnnotationMetadata.java +++ b/core/src/main/java/io/micronaut/core/annotation/AnnotationMetadata.java @@ -19,6 +19,7 @@ import io.micronaut.core.type.Argument; import io.micronaut.core.util.ArgumentUtils; import io.micronaut.core.util.ArrayUtils; +import io.micronaut.core.util.StringUtils; import io.micronaut.core.value.OptionalValues; import javax.annotation.Nonnull; @@ -122,7 +123,7 @@ public interface AnnotationMetadata extends AnnotationSource { * @param The required generic type * @return An optional value */ - default @Nonnull Optional getDefaultValue(@Nonnull String annotation, @Nonnull String member, @Nonnull Argument requiredType) { + default Optional getDefaultValue(@Nonnull String annotation, @Nonnull String member, @Nonnull Argument requiredType) { return Optional.empty(); } @@ -202,7 +203,7 @@ default boolean hasDeclaredStereotype(@Nullable String annotation) { * @param The required generic type * @return An optional value */ - default @Nonnull Optional getDefaultValue(@Nonnull String annotation, @Nonnull String member, @Nonnull Class requiredType) { + default Optional getDefaultValue(@Nonnull String annotation, @Nonnull String member, @Nonnull Class requiredType) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("member", member); ArgumentUtils.requireNonNull("requiredType", requiredType); @@ -219,7 +220,7 @@ default boolean hasDeclaredStereotype(@Nullable String annotation) { * @param The required generic type * @return An optional value */ - default @Nonnull Optional getDefaultValue(@Nonnull Class annotation, @Nonnull String member, @Nonnull Argument requiredType) { + default Optional getDefaultValue(@Nonnull Class annotation, @Nonnull String member, @Nonnull Argument requiredType) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("member", member); ArgumentUtils.requireNonNull("requiredType", requiredType); @@ -260,7 +261,7 @@ default boolean isDeclaredAnnotationPresent(@Nonnull Class * @param The required generic type * @return An optional value */ - default @Nonnull Optional getDefaultValue(@Nonnull Class annotation, @Nonnull String member, @Nonnull Class requiredType) { + default Optional getDefaultValue(@Nonnull Class annotation, @Nonnull String member, @Nonnull Class requiredType) { ArgumentUtils.requireNonNull("annotation", annotation); return getDefaultValue(annotation.getName(), member, requiredType); } @@ -274,7 +275,7 @@ default boolean isDeclaredAnnotationPresent(@Nonnull Class * @param The value * @return An {@link Optional} of the value */ - default @Nonnull Optional getValue(@Nonnull Class annotation, @Nonnull String member, @Nonnull Class requiredType) { + default Optional getValue(@Nonnull Class annotation, @Nonnull String member, @Nonnull Class requiredType) { ArgumentUtils.requireNonNull("requiredType", requiredType); return getValue(annotation, member, Argument.of(requiredType)); } @@ -289,7 +290,7 @@ default boolean isDeclaredAnnotationPresent(@Nonnull Class * @param The value * @return An {@link Optional} of the value */ - default @Nonnull Optional getValue(@Nonnull Class annotation, @Nonnull String member, @Nonnull Argument requiredType) { + default Optional getValue(@Nonnull Class annotation, @Nonnull String member, @Nonnull Argument requiredType) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("member", member); ArgumentUtils.requireNonNull("requiredType", requiredType); @@ -321,7 +322,7 @@ default boolean isDeclaredAnnotationPresent(@Nonnull Class * @param stereotype The stereotype * @return The annotation name */ - default @Nonnull Optional getAnnotationNameByStereotype(@Nullable String stereotype) { + default Optional getAnnotationNameByStereotype(@Nullable String stereotype) { return getAnnotationNamesByStereotype(stereotype).stream().findFirst(); } @@ -331,7 +332,7 @@ default boolean isDeclaredAnnotationPresent(@Nonnull Class * @param stereotype The stereotype * @return The annotation name */ - default @Nonnull Optional getDeclaredAnnotationNameByStereotype(@Nullable String stereotype) { + default Optional getDeclaredAnnotationNameByStereotype(@Nullable String stereotype) { return getDeclaredAnnotationNamesByStereotype(stereotype).stream().findFirst(); } @@ -341,7 +342,7 @@ default boolean isDeclaredAnnotationPresent(@Nonnull Class * @param stereotype The stereotype * @return The annotation name */ - default @Nonnull Optional> getAnnotationTypeByStereotype(@Nonnull Class stereotype) { + default Optional> getAnnotationTypeByStereotype(@Nonnull Class stereotype) { ArgumentUtils.requireNonNull("stereotype", stereotype); return getAnnotationTypeByStereotype(stereotype.getName()); @@ -353,7 +354,7 @@ default boolean isDeclaredAnnotationPresent(@Nonnull Class * @param stereotype The stereotype * @return The annotation name */ - default @Nonnull Optional> getDeclaredAnnotationTypeByStereotype(@Nonnull Class stereotype) { + default Optional> getDeclaredAnnotationTypeByStereotype(@Nonnull Class stereotype) { ArgumentUtils.requireNonNull("stereotype", stereotype); return getDeclaredAnnotationTypeByStereotype(stereotype.getName()); } @@ -364,7 +365,7 @@ default boolean isDeclaredAnnotationPresent(@Nonnull Class * @param stereotype The stereotype * @return The annotation name */ - default @Nonnull Optional> getDeclaredAnnotationTypeByStereotype(@Nullable String stereotype) { + default Optional> getDeclaredAnnotationTypeByStereotype(@Nullable String stereotype) { return getDeclaredAnnotationNameByStereotype(stereotype).flatMap(this::getAnnotationType); } @@ -373,7 +374,7 @@ default boolean isDeclaredAnnotationPresent(@Nonnull Class * @param name The type name * @return The type if present */ - default @Nonnull Optional> getAnnotationType(@Nonnull String name) { + default Optional> getAnnotationType(@Nonnull String name) { ArgumentUtils.requireNonNull("name", name); final Optional aClass = ClassUtils.forName(name, getClass().getClassLoader()); return aClass.flatMap((Function>>) aClass1 -> { @@ -391,7 +392,7 @@ default boolean isDeclaredAnnotationPresent(@Nonnull Class * @param stereotype The stereotype * @return The annotation name */ - default @Nonnull Optional> getAnnotationTypeByStereotype(@Nullable String stereotype) { + default Optional> getAnnotationTypeByStereotype(@Nullable String stereotype) { return getAnnotationNameByStereotype(stereotype).flatMap(this::getAnnotationType); } @@ -401,7 +402,7 @@ default boolean isDeclaredAnnotationPresent(@Nonnull Class * @param stereotype The stereotype * @return The annotation name */ - default @Nonnull Optional getAnnotationNameByStereotype(@Nonnull Class stereotype) { + default Optional getAnnotationNameByStereotype(@Nonnull Class stereotype) { ArgumentUtils.requireNonNull("stereotype", stereotype); return getAnnotationNameByStereotype(stereotype.getName()); } @@ -456,7 +457,7 @@ default boolean isDeclaredAnnotationPresent(@Nonnull Class * @return The {@link AnnotationValue} */ @Override - default @Nonnull Optional> findAnnotation(@Nonnull Class annotationClass) { + default Optional> findAnnotation(@Nonnull Class annotationClass) { ArgumentUtils.requireNonNull("annotationClass", annotationClass); Repeatable repeatable = annotationClass.getAnnotation(Repeatable.class); if (repeatable != null) { @@ -475,7 +476,7 @@ default boolean isDeclaredAnnotationPresent(@Nonnull Class * {@inheritDoc} */ @Override - default @Nonnull Optional> findDeclaredAnnotation(@Nonnull Class annotationClass) { + default Optional> findDeclaredAnnotation(@Nonnull Class annotationClass) { ArgumentUtils.requireNonNull("annotationClass", annotationClass); Repeatable repeatable = annotationClass.getAnnotation(Repeatable.class); if (repeatable != null) { @@ -499,7 +500,7 @@ default boolean isDeclaredAnnotationPresent(@Nonnull Class * @param The value * @return An {@link Optional} of the value */ - default @Nonnull Optional getValue(@Nonnull String annotation, @Nonnull String member, @Nonnull Class requiredType) { + default Optional getValue(@Nonnull String annotation, @Nonnull String member, @Nonnull Class requiredType) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("member", member); ArgumentUtils.requireNonNull("requiredType", requiredType); @@ -516,7 +517,7 @@ default boolean isDeclaredAnnotationPresent(@Nonnull Class * @param The value * @return An {@link Optional} of the value */ - default @Nonnull Optional getValue(@Nonnull String annotation, @Nonnull String member, @Nonnull Argument requiredType) { + default Optional getValue(@Nonnull String annotation, @Nonnull String member, @Nonnull Argument requiredType) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("member", member); ArgumentUtils.requireNonNull("requiredType", requiredType); @@ -537,7 +538,7 @@ default boolean isDeclaredAnnotationPresent(@Nonnull Class * @param member The member * @return THe {@link OptionalLong} value */ - default @Nonnull OptionalLong longValue(@Nonnull String annotation, @Nonnull String member) { + default OptionalLong longValue(@Nonnull String annotation, @Nonnull String member) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("member", member); @@ -551,7 +552,7 @@ default boolean isDeclaredAnnotationPresent(@Nonnull Class * @param annotation The annotation * @return An {@link Optional} class */ - default @Nonnull Optional classValue(@Nonnull String annotation) { + default Optional classValue(@Nonnull String annotation) { ArgumentUtils.requireNonNull("annotation", annotation); return classValue(annotation, VALUE_MEMBER); } @@ -563,11 +564,13 @@ default boolean isDeclaredAnnotationPresent(@Nonnull Class * @param member The annotation member * @return An {@link Optional} class */ - default @Nonnull Optional classValue(@Nonnull String annotation, @Nonnull String member) { + default Optional classValue(@Nonnull String annotation, @Nonnull String member) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("member", member); - return getValue(annotation, member, Class.class); + Optional value = getValue(annotation, member, Class.class); + //noinspection unchecked + return value; } /** @@ -576,10 +579,10 @@ default boolean isDeclaredAnnotationPresent(@Nonnull Class * @param annotation The annotation * @return An {@link Optional} class */ - default @Nonnull Optional classValue(@Nonnull Class annotation) { + default Optional classValue(@Nonnull Class annotation) { ArgumentUtils.requireNonNull("annotation", annotation); - return classValue(annotation.getName()); + return classValue(annotation, VALUE_MEMBER); } /** @@ -589,13 +592,15 @@ default boolean isDeclaredAnnotationPresent(@Nonnull Class * @param member The annotation member * @return An {@link Optional} class */ - default @Nonnull Optional classValue(@Nonnull Class annotation, @Nonnull String member) { + default Optional classValue(@Nonnull Class annotation, @Nonnull String member) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("member", member); return classValue(annotation.getName(), member); } + + /** * The value as an {@link OptionalInt} for the given annotation and member. * @@ -603,7 +608,7 @@ default boolean isDeclaredAnnotationPresent(@Nonnull Class * @param member The member * @return THe {@link OptionalInt} value */ - default @Nonnull OptionalInt intValue(@Nonnull String annotation, @Nonnull String member) { + default OptionalInt intValue(@Nonnull String annotation, @Nonnull String member) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("member", member); @@ -611,6 +616,97 @@ default boolean isDeclaredAnnotationPresent(@Nonnull Class return result.map(OptionalInt::of).orElseGet(OptionalInt::empty); } + /** + * The value as an {@link OptionalInt} for the given annotation and member. + * + * @param annotation The annotation + * @param member The member + * @return THe {@link OptionalInt} value + */ + default OptionalInt intValue(@Nonnull Class annotation, @Nonnull String member) { + ArgumentUtils.requireNonNull("annotation", annotation); + return intValue(annotation.getName(), member); + } + + /** + * The value as an {@link OptionalInt} for the given annotation and member. + * + * @param annotation The annotation + * @return THe {@link OptionalInt} value + */ + default OptionalInt intValue(@Nonnull Class annotation) { + ArgumentUtils.requireNonNull("annotation", annotation); + return intValue(annotation, VALUE_MEMBER); + } + + /** + * The value as an optional string for the given annotation and member. + * + * @param annotation The annotation + * @param member The member + * @return The string value if it is present + */ + default Optional stringValue(@Nonnull String annotation, @Nonnull String member) { + ArgumentUtils.requireNonNull("annotation", annotation); + ArgumentUtils.requireNonNull("member", member); + + return getValue(annotation, member, String.class); + } + + /** + * The value as an optional string for the given annotation and member. + * + * @param annotation The annotation + * @param member The member + * @return The string value if it is present + */ + default Optional stringValue(@Nonnull Class annotation, @Nonnull String member) { + ArgumentUtils.requireNonNull("annotation", annotation); + return stringValue(annotation.getName(), member); + } + + /** + * The values as string array for the given annotation and member. + * + * @param annotation The annotation + * @param member The member + * @return The string values if it is present + */ + default @Nonnull String[] stringValues(@Nonnull Class annotation, @Nonnull String member) { + return StringUtils.EMPTY_STRING_ARRAY; + } + + /** + * The values as string array for the given annotation and member. + * + * @param annotation The annotation + * @return The string values if it is present + */ + default @Nonnull String[] stringValues(@Nonnull Class annotation) { + return stringValues(annotation, VALUE_MEMBER); + } + + /** + * The value as an optional string for the given annotation and member. + * + * @param annotation The annotation + * @return The string value if it is present + */ + default @Nonnull Optional stringValue(@Nonnull Class annotation) { + ArgumentUtils.requireNonNull("annotation", annotation); + return stringValue(annotation, VALUE_MEMBER); + } + + /** + * The value as an optional string for the given annotation and member. + * + * @param annotation The annotation + * @return The string value if it is present + */ + default @Nonnull Optional stringValue(@Nonnull String annotation) { + return stringValue(annotation, VALUE_MEMBER); + } + /** * The value as an {@link OptionalDouble} for the given annotation and member. * @@ -626,6 +722,29 @@ default boolean isDeclaredAnnotationPresent(@Nonnull Class return result.map(OptionalDouble::of).orElseGet(OptionalDouble::empty); } + /** + * The value as an {@link OptionalDouble} for the given annotation and member. + * + * @param annotation The annotation + * @param member The member + * @return THe {@link OptionalDouble} value + */ + default @Nonnull OptionalDouble doubleValue(@Nonnull Class annotation, @Nonnull String member) { + ArgumentUtils.requireNonNull("annotation", annotation); + return doubleValue(annotation.getName(), member); + } + + /** + * The value as an {@link OptionalDouble} for the given annotation and member. + * + * @param annotation The annotation + * @return THe {@link OptionalDouble} value + */ + default @Nonnull OptionalDouble doubleValue(@Nonnull Class annotation) { + ArgumentUtils.requireNonNull("annotation", annotation); + return doubleValue(annotation, VALUE_MEMBER); + } + /** * Get the value of default "value" the given annotation. * @@ -709,7 +828,7 @@ default boolean isPresent(@Nonnull String annotation, @Nonnull String member) { } /** - * Returns whether the value of the given member is true. + * Returns whether the value of the given member is present. * * @param annotation The annotation class * @param member The annotation member diff --git a/core/src/main/java/io/micronaut/core/annotation/AnnotationMetadataDelegate.java b/core/src/main/java/io/micronaut/core/annotation/AnnotationMetadataDelegate.java index 64f7fffbe08..b4e4b94fec9 100644 --- a/core/src/main/java/io/micronaut/core/annotation/AnnotationMetadataDelegate.java +++ b/core/src/main/java/io/micronaut/core/annotation/AnnotationMetadataDelegate.java @@ -31,6 +31,65 @@ * @since 1.0 */ public interface AnnotationMetadataDelegate extends AnnotationMetadataProvider, AnnotationMetadata { + @Nonnull + @Override + default String[] stringValues(@Nonnull Class annotation, @Nonnull String member) { + return getAnnotationMetadata().stringValues(annotation, member); + } + + @Nonnull + @Override + default String[] stringValues(@Nonnull Class annotation) { + return getAnnotationMetadata().stringValues(annotation, AnnotationMetadata.VALUE_MEMBER); + } + + @Nonnull + @Override + default OptionalInt intValue(@Nonnull Class annotation, @Nonnull String member) { + return getAnnotationMetadata().intValue(annotation, member); + } + + @Nonnull + @Override + default OptionalInt intValue(@Nonnull Class annotation) { + return getAnnotationMetadata().intValue(annotation); + } + + @Nonnull + @Override + default Optional stringValue(@Nonnull String annotation, @Nonnull String member) { + return getAnnotationMetadata().stringValue(annotation, member); + } + + @Nonnull + @Override + default Optional stringValue(@Nonnull Class annotation, @Nonnull String member) { + return getAnnotationMetadata().stringValue(annotation, member); + } + + @Nonnull + @Override + default Optional stringValue(@Nonnull Class annotation) { + return getAnnotationMetadata().stringValue(annotation); + } + + @Nonnull + @Override + default Optional stringValue(@Nonnull String annotation) { + return getAnnotationMetadata().stringValue(annotation); + } + + @Nonnull + @Override + default OptionalDouble doubleValue(@Nonnull Class annotation, @Nonnull String member) { + return getAnnotationMetadata().doubleValue(annotation, member); + } + + @Nonnull + @Override + default OptionalDouble doubleValue(@Nonnull Class annotation) { + return getAnnotationMetadata().doubleValue(annotation); + } @Override default @Nonnull Optional getValue(@Nonnull String annotation, @Nonnull Argument requiredType) { diff --git a/core/src/main/java/io/micronaut/core/annotation/AnnotationValue.java b/core/src/main/java/io/micronaut/core/annotation/AnnotationValue.java index c2c5717063a..8cc2f58e3e0 100644 --- a/core/src/main/java/io/micronaut/core/annotation/AnnotationValue.java +++ b/core/src/main/java/io/micronaut/core/annotation/AnnotationValue.java @@ -19,19 +19,26 @@ import io.micronaut.core.convert.ConversionContext; import io.micronaut.core.convert.ConversionService; import io.micronaut.core.convert.value.ConvertibleValues; +import io.micronaut.core.reflect.ClassUtils; +import io.micronaut.core.reflect.ReflectionUtils; import io.micronaut.core.type.Argument; +import io.micronaut.core.util.ArgumentUtils; import io.micronaut.core.util.ArrayUtils; import io.micronaut.core.util.CollectionUtils; -import io.micronaut.core.value.ValueResolver; +import io.micronaut.core.util.StringUtils; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.lang.annotation.Annotation; +import java.lang.reflect.Array; import java.util.*; +import java.util.function.Function; +import java.util.stream.Stream; /** * A runtime representation of the an annotation and its values. * - *

This class implements the {@link ValueResolver} interface and methods such as {@link ValueResolver#get(CharSequence, Class)} can be used to retrieve the values of annotation members.

+ *

This class implements the {@link AnnotationValueResolver} interface and methods such as {@link AnnotationValueResolver#get(CharSequence, Class)} can be used to retrieve the values of annotation members.

* *

If a member is not present then the methods of the class will attempt to resolve the default value for a given annotation member. In this sense the behaviour of this class is similar to how * a implementation of {@link Annotation} behaves.

@@ -40,12 +47,13 @@ * @since 1.0 * @param The annotation type */ -public class AnnotationValue implements ValueResolver { +public class AnnotationValue implements AnnotationValueResolver { private final String annotationName; private final ConvertibleValues convertibleValues; private final Map values; private final Map defaultValues; + private final Function valueMapper; /** * @param annotationName The annotation name @@ -57,6 +65,7 @@ public AnnotationValue(String annotationName, Map values) this.convertibleValues = newConvertibleValues(values); this.values = values; this.defaultValues = Collections.emptyMap(); + this.valueMapper = null; } /** @@ -70,6 +79,7 @@ public AnnotationValue(String annotationName, Map values, this.convertibleValues = newConvertibleValues(values); this.values = values; this.defaultValues = defaultValues != null ? defaultValues : Collections.emptyMap(); + this.valueMapper = null; } /** @@ -82,6 +92,7 @@ public AnnotationValue(String annotationName) { this.convertibleValues = ConvertibleValues.EMPTY; this.values = Collections.emptyMap(); this.defaultValues = Collections.emptyMap(); + this.valueMapper = null; } /** @@ -95,6 +106,7 @@ public AnnotationValue(String annotationName, ConvertibleValues converti this.values = new HashMap<>(existing.size()); this.values.putAll(existing); this.defaultValues = Collections.emptyMap(); + this.valueMapper = null; } /** @@ -102,14 +114,358 @@ public AnnotationValue(String annotationName, ConvertibleValues converti * @param target The target * @param defaultValues The default values * @param convertibleValues The convertible values + * @param valueMapper The value mapper */ @Internal @UsedByGeneratedCode - protected AnnotationValue(AnnotationValue target, Map defaultValues, ConvertibleValues convertibleValues) { + protected AnnotationValue(AnnotationValue target, Map defaultValues, ConvertibleValues convertibleValues, Function valueMapper) { this.annotationName = target.annotationName; this.defaultValues = defaultValues != null ? defaultValues : target.defaultValues; this.values = target.values; this.convertibleValues = convertibleValues; + this.valueMapper = valueMapper; + } + + + /** + * The value of the annotation as a Class. + * + * @return An {@link Optional} class + */ + @Override + public @Nonnull Optional> classValue() { + return classValue(AnnotationMetadata.VALUE_MEMBER); + } + + /** + * The value of the given annotation member as a Class. + * + * @param member The annotation member + * @return An {@link Optional} class + */ + @Override + @SuppressWarnings("unchecked") + public Optional> classValue(@Nonnull String member) { + return classValue(member, valueMapper); + } + + @Nonnull + @Override + public String[] stringValues(@Nonnull String member) { + Function valueMapper = this.valueMapper; + return stringValues(member, valueMapper); + } + + /** + * The string values for the given member and mapper. + * @param member The member + * @param valueMapper The mapper + * @return The string values + */ + public String[] stringValues(@Nonnull String member, Function valueMapper) { + if (StringUtils.isNotEmpty(member)) { + Object o = values.get(member); + String[] strs = resolveStringValues(o, valueMapper); + if (strs != null) { + return strs; + } + } + return StringUtils.EMPTY_STRING_ARRAY; + } + + @Override + public Class[] classValues(@Nonnull String member) { + if (StringUtils.isNotEmpty(member)) { + Object o = values.get(member); + Class[] type = resolveClassValues(o); + if (type != null) { + return type; + } + + } + return ReflectionUtils.EMPTY_CLASS_ARRAY; + } + + /** + * The value of the given annotation member as a Class. + * + * @param member The annotation member + * @param valueMapper The raw value mapper + * @return An {@link Optional} class + */ + public Optional> classValue(@Nonnull String member, @Nullable Function valueMapper) { + if (StringUtils.isNotEmpty(member)) { + Object o = getRawSingleValue(member, valueMapper); + if (o instanceof AnnotationClassValue) { + return ((AnnotationClassValue) o).getType(); + } else if (o instanceof Class) { + return Optional.of(((Class) o)); + } + } + return Optional.empty(); + } + + /** + * The integer value of the given member. + * + * @param member The annotation member + * @return An {@link OptionalInt} + */ + @Override + public OptionalInt intValue(@Nonnull String member) { + return intValue(member, valueMapper); + } + + /** + * The integer value of the given member. + * + * @param member The annotation member + * @param valueMapper The value mapper + * @return An {@link OptionalInt} + */ + public OptionalInt intValue(@Nonnull String member, @Nullable Function valueMapper) { + if (StringUtils.isNotEmpty(member)) { + Object o = getRawSingleValue(member, valueMapper); + if (o instanceof Number) { + return OptionalInt.of(((Number) o).intValue()); + } else if (o instanceof CharSequence) { + try { + return OptionalInt.of(Integer.parseInt(o.toString())); + } catch (NumberFormatException e) { + return OptionalInt.empty(); + } + } + } + return OptionalInt.empty(); + } + + /** + * The integer value of the given member. + * + * @return An {@link OptionalInt} + */ + @Override + public OptionalInt intValue() { + return intValue(AnnotationMetadata.VALUE_MEMBER); + } + + @Override + public OptionalLong longValue(@Nonnull String member) { + return longValue(member, null); + } + + /** + * The integer value of the given member. + * + * @param member The annotation member + * @param valueMapper The value mapper + * @return An {@link OptionalInt} + */ + public OptionalLong longValue(@Nonnull String member, @Nullable Function valueMapper) { + if (StringUtils.isNotEmpty(member)) { + Object o = getRawSingleValue(member, valueMapper); + if (o instanceof Number) { + return OptionalLong.of((((Number) o).longValue())); + } else if (o instanceof CharSequence) { + try { + return OptionalLong.of(Long.parseLong(o.toString())); + } catch (NumberFormatException e) { + return OptionalLong.empty(); + } + } + } + return OptionalLong.empty(); + } + + /** + * The double value of the given member. + * + * @param member The annotation member + * @return An {@link OptionalDouble} + */ + @Override + public OptionalDouble doubleValue(@Nonnull String member) { + return doubleValue(member, valueMapper); + } + + /** + * The double value of the given member. + * + * @param member The annotation member + * @param valueMapper The value mapper + * @return An {@link OptionalDouble} + */ + public OptionalDouble doubleValue(@Nonnull String member, @Nullable Function valueMapper) { + if (StringUtils.isNotEmpty(member)) { + Object o = getRawSingleValue(member, valueMapper); + if (o instanceof Number) { + return OptionalDouble.of(((Number) o).doubleValue()); + } else if (o instanceof CharSequence) { + try { + return OptionalDouble.of(Double.parseDouble(o.toString())); + } catch (NumberFormatException e) { + return OptionalDouble.empty(); + } + } + } + return OptionalDouble.empty(); + } + + /** + * The double value of the given member. + * + * @return An {@link OptionalDouble} + */ + @Override + public OptionalDouble doubleValue() { + return doubleValue(AnnotationMetadata.VALUE_MEMBER); + } + + /** + * The string value of the given member. + * + * @param member The annotation member + * @return An {@link OptionalInt} + */ + @Override + public Optional stringValue(@Nonnull String member) { + if (StringUtils.isNotEmpty(member)) { + Object o = getRawSingleValue(member, valueMapper); + if (o != null) { + return Optional.of(o.toString()); + } + } + return Optional.empty(); + } + + /** + * The string value of the given member. + * + * @param member The annotation member + * @param valueMapper An optional raw value mapper + * @return An {@link OptionalInt} + */ + public Optional stringValue(@Nonnull String member, @Nullable Function valueMapper) { + if (StringUtils.isNotEmpty(member)) { + Object o = getRawSingleValue(member, valueMapper); + if (o != null) { + return Optional.of(o.toString()); + } + } + return Optional.empty(); + } + + /** + * The double value of the given member. + * + * @return An {@link OptionalInt} + */ + @Override + public Optional stringValue() { + return stringValue(AnnotationMetadata.VALUE_MEMBER); + } + + /** + * Is the given member present. + * @param member The member + * @return True if it is + */ + @Override + public final boolean isPresent(CharSequence member) { + if (StringUtils.isNotEmpty(member)) { + return values.containsKey(member); + } + return false; + } + + /** + * @return Is the value of the annotation true. + */ + @Override + public boolean isTrue() { + return isTrue(AnnotationMetadata.VALUE_MEMBER); + } + + /** + * @param member The member + * + * @return Is the value of the annotation true. + */ + @Override + public boolean isTrue(String member) { + return isTrue(member, valueMapper); + } + + /** + * @param member The member + * @param valueMapper The value mapper + * @return Is the value of the annotation true. + */ + public boolean isTrue(@Nonnull String member, @Nullable Function valueMapper) { + if (StringUtils.isNotEmpty(member)) { + Object o = getRawSingleValue(member, valueMapper); + if (o instanceof Boolean) { + return (Boolean) o; + } else if (o != null) { + return StringUtils.isTrue(o.toString()); + } + } + return false; + } + + + /** + * @return Is the value of the annotation true. + */ + @Override + public boolean isFalse() { + return !isTrue(AnnotationMetadata.VALUE_MEMBER); + } + + /** + * @param member The member + * + * @return Is the value of the annotation true. + */ + @Override + public boolean isFalse(String member) { + return !isTrue(member); + } + + /** + * The value of the given annotation member as a Class. + * + * @param member The annotation member + * @param requiredType The required type + * @return An {@link Optional} class + * @param The required type + */ + @Override + @SuppressWarnings("unchecked") + public Optional> classValue(@Nonnull String member, @Nonnull Class requiredType) { + ArgumentUtils.requireNonNull("requiredType", requiredType); + if (StringUtils.isNotEmpty(member)) { + Object o = getRawSingleValue(member, valueMapper); + if (o instanceof AnnotationClassValue) { + Class t = ((AnnotationClassValue) o).getType().orElse(null); + if (t != null && requiredType.isAssignableFrom(t)) { + return Optional.of((Class) t); + } + return Optional.empty(); + } else if (o instanceof Class) { + Class t = (Class) o; + if (requiredType.isAssignableFrom(t)) { + return Optional.of((Class) t); + } + return Optional.empty(); + } else if (o != null) { + Class t = ClassUtils.forName(o.toString(), getClass().getClassLoader()).orElse(null); + if (t != null && requiredType.isAssignableFrom(t)) { + return Optional.of((Class) t); + } + } + } + return Optional.empty(); } /** @@ -127,7 +483,7 @@ protected AnnotationValue(AnnotationValue target, Map default * @return True if it is */ public final boolean contains(String member) { - return this.values.containsKey(member); + return isPresent(member); } /** @@ -142,6 +498,7 @@ public final boolean contains(String member) { /** * @return The attribute values */ + @Override @SuppressWarnings("unchecked") public @Nonnull Map getValues() { return Collections.unmodifiableMap(values); @@ -234,7 +591,15 @@ public final Optional getValue(Class type) { * @return The result */ public @Nonnull final List> getAnnotations(String member, Class type) { - AnnotationValue[] values = get(member, AnnotationValue[].class).orElse(null); + ArgumentUtils.requireNonNull("member", member); + ArgumentUtils.requireNonNull("type", type); + Object v = values.get(member); + AnnotationValue[] values = null; + if (v instanceof AnnotationValue) { + values = new AnnotationValue[] {(AnnotationValue) v}; + } else if (v instanceof AnnotationValue[]) { + values = (AnnotationValue[]) v; + } if (ArrayUtils.isNotEmpty(values)) { List> list = new ArrayList<>(values.length); String typeName = type.getName(); @@ -252,6 +617,26 @@ public final Optional getValue(Class type) { return Collections.emptyList(); } + /** + * Gets a list of {@link AnnotationValue} for the given member. + * + * @param member The member + * @param The type + * @throws IllegalStateException If no member is available that conforms to the given name and type + * @return The result + */ + @SuppressWarnings("unchecked") + public @Nonnull final List> getAnnotations(String member) { + ArgumentUtils.requireNonNull("member", member); + Object v = values.get(member); + if (v instanceof AnnotationValue) { + return Collections.singletonList((AnnotationValue) v); + } else if (v instanceof AnnotationValue[]) { + return Arrays.asList((AnnotationValue[]) v); + } + return Collections.emptyList(); + } + /** * Gets a list of {@link AnnotationValue} for the given member. * @@ -329,6 +714,81 @@ public static AnnotationValueBuilder builder(Class return new AnnotationValueBuilder<>(annotation); } + /** + * The string values for the given value. + * @param value The value + * @param valueMapper The value mapper + * @return The string[] or null + */ + @Internal + public static @Nullable String[] resolveStringValues(@Nullable Object value, @Nullable Function valueMapper) { + if (value == null) { + return null; + } + if (valueMapper != null) { + value = valueMapper.apply(value); + } + if (value instanceof CharSequence) { + return new String[] { value.toString() }; + } else if (value instanceof String[]) { + if (valueMapper != null) { + String[] strs = (String[]) value; + String[] newStrs = new String[strs.length]; + for (int i = 0; i < strs.length; i++) { + String str = strs[i]; + newStrs[i] = valueMapper.apply(str).toString(); + } + return newStrs; + } else { + return (String[]) value; + } + } else if (value != null) { + if (value.getClass().isArray()) { + int len = Array.getLength(value); + String[] newArray = new String[len]; + for (int i = 0; i < newArray.length; i++) { + Object entry = Array.get(value, i); + if (entry != null) { + newArray[i] = entry.toString(); + } + } + return newArray; + } else { + return new String[] { value.toString() }; + } + } + return null; + } + + /** + * The classes class values for the given value. + * @param value The value + * @return The class values or null + */ + @Internal + public static @Nullable Class[] resolveClassValues(@Nullable Object value) { + // conditional branches ordered from most likely to least likely + // generally at runtime values are always AnnotationClassValue + // A class can be present at compilation time + if (value instanceof AnnotationClassValue) { + Class type = ((AnnotationClassValue) value).getType().orElse(null); + if (type != null) { + return new Class[] { type }; + } + } else if (value instanceof AnnotationClassValue[]) { + AnnotationClassValue[] values = (AnnotationClassValue[]) value; + return Arrays.stream(values).flatMap(av -> { + Optional> t = av.getType(); + return t.map(Stream::of).orElse(Stream.empty()); + }).toArray(Class[]::new); + } else if (value instanceof Class) { + return new Class[] {(Class) value}; + } else if (value instanceof Class[]) { + return (Class[]) value; + } + return null; + } + /** * Subclasses can override to provide a custom convertible values instance. * @@ -343,4 +803,19 @@ private ConvertibleValues newConvertibleValues(Map } } + private @Nullable Object getRawSingleValue(@Nonnull String member, Function valueMapper) { + Object rawValue = values.get(member); + if (rawValue != null) { + if (rawValue.getClass().isArray()) { + int len = Array.getLength(rawValue); + if (len > 0) { + rawValue = Array.get(rawValue, 0); + } + } + } + if (valueMapper != null && rawValue instanceof String) { + return valueMapper.apply(rawValue); + } + return rawValue; + } } diff --git a/core/src/main/java/io/micronaut/core/annotation/AnnotationValueResolver.java b/core/src/main/java/io/micronaut/core/annotation/AnnotationValueResolver.java new file mode 100644 index 00000000000..6848e32c9e1 --- /dev/null +++ b/core/src/main/java/io/micronaut/core/annotation/AnnotationValueResolver.java @@ -0,0 +1,201 @@ +/* + * Copyright 2017-2019 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 + * + * http://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.core.annotation; + +import io.micronaut.core.value.ValueResolver; + +import javax.annotation.Nonnull; +import java.util.*; + +/** + * Interface for types that resolve annotation values. + * + * @author graemerocher + * @since 1.0.3 + */ +public interface AnnotationValueResolver extends ValueResolver { + /** + * The value of the annotation as a Class. + * + * @return An {@link Optional} class + */ + default Optional> classValue() { + return classValue(AnnotationMetadata.VALUE_MEMBER); + } + + /** + * The value of the given annotation member as a Class. + * + * @param member The annotation member + * @return An {@link Optional} class + */ + Optional> classValue(@Nonnull String member); + + /** + * The value of the annotation as a Class. + * + * @return An {@link Optional} class + */ + @Nonnull + default Class[] classValues() { + return classValues(AnnotationMetadata.VALUE_MEMBER); + } + + /** + * The value of the given annotation member as a Class. + * + * @param member The annotation member + * @return An {@link Optional} class + */ + @Nonnull Class[] classValues(@Nonnull String member); + + /** + * The integer value of the given member. + * + * @param member The annotation member + * @return An {@link OptionalInt} + */ + OptionalInt intValue(@Nonnull String member); + + /** + * The integer value of the given member. + * + * @return An {@link OptionalInt} + */ + default OptionalInt intValue() { + return intValue(AnnotationMetadata.VALUE_MEMBER); + } + + /** + * The long value of the given member. + * + * @param member The annotation member + * @return An {@link OptionalLong} + */ + OptionalLong longValue(@Nonnull String member); + + /** + * The integer value of the given member. + * + * @return An {@link OptionalLong} + */ + default OptionalLong longValue() { + return longValue(AnnotationMetadata.VALUE_MEMBER); + } + + /** + * The double value of the given member. + * + * @param member The annotation member + * @return An {@link OptionalDouble} + */ + OptionalDouble doubleValue(@Nonnull String member); + + /** + * The double value of the given member. + * + * @return An {@link OptionalDouble} + */ + default OptionalDouble doubleValue() { + return doubleValue(AnnotationMetadata.VALUE_MEMBER); + } + + /** + * The string value of the given member. + * + * @param member The annotation member + * @return An {@link OptionalInt} + */ + Optional stringValue(@Nonnull String member); + + /** + * The double value of the given member. + * + * @return An {@link OptionalInt} + */ + default Optional stringValue() { + return stringValue(AnnotationMetadata.VALUE_MEMBER); + } + + /** + * The string value of the given member. + * + * @param member The annotation member + * @return An {@link OptionalInt} + */ + @Nonnull String[] stringValues(@Nonnull String member); + + /** + * The double value of the given member. + * + * @return An {@link OptionalInt} + */ + default @Nonnull String[] stringValues() { + return stringValues(AnnotationMetadata.VALUE_MEMBER); + } + + /** + * Is the given member present. + * @param member The member + * @return True if it is + */ + boolean isPresent(CharSequence member); + + /** + * @return Is the value of the annotation true. + */ + default boolean isTrue() { + return isTrue(AnnotationMetadata.VALUE_MEMBER); + } + + /** + * @param member The member + * + * @return Is the value of the annotation true. + */ + boolean isTrue(String member); + + /** + * @return Is the value of the annotation true. + */ + default boolean isFalse() { + return isFalse(AnnotationMetadata.VALUE_MEMBER); + } + + /** + * @param member The member + * + * @return Is the value of the annotation true. + */ + boolean isFalse(String member); + + /** + * The value of the given annotation member as a Class. + * + * @param member The annotation member + * @param requiredType The required type + * @return An {@link Optional} class + * @param The required type + */ + Optional> classValue(@Nonnull String member, @Nonnull Class requiredType); + + /** + * @return The attribute values + */ + @Nonnull + Map getValues(); +} diff --git a/core/src/main/java/io/micronaut/core/convert/DefaultConversionService.java b/core/src/main/java/io/micronaut/core/convert/DefaultConversionService.java index 4a83a6ee26b..3fd5e5d5ba5 100644 --- a/core/src/main/java/io/micronaut/core/convert/DefaultConversionService.java +++ b/core/src/main/java/io/micronaut/core/convert/DefaultConversionService.java @@ -67,9 +67,13 @@ */ public class DefaultConversionService implements ConversionService { - private static final int CACHE_MAX = 60; + private static final int CACHE_MAX = 150; + private static final TypeConverter UNCONVERTIBLE = (object, targetType, context) -> Optional.empty(); + private final Map typeConverters = new ConcurrentHashMap<>(); - private final Map converterCache = new ConcurrentLinkedHashMap.Builder().maximumWeightedCapacity(CACHE_MAX).build(); + private final Map converterCache = new ConcurrentLinkedHashMap.Builder() + .maximumWeightedCapacity(CACHE_MAX) + .build(); /** * Constructor. @@ -88,7 +92,7 @@ public Optional convert(Object object, Class targetType, ConversionCon return Optional.of((T) object); } Class sourceType = object.getClass(); - targetType = ReflectionUtils.getWrapperType(targetType); + targetType = targetType.isPrimitive() ? ReflectionUtils.getWrapperType(targetType) : targetType; if (targetType.isInstance(object) && !Iterable.class.isInstance(object) && !Map.class.isInstance(object)) { return Optional.of((T) object); @@ -104,9 +108,16 @@ public Optional convert(Object object, Class targetType, ConversionCon return Optional.empty(); } else { converterCache.put(pair, typeConverter); + if (typeConverter == UNCONVERTIBLE) { + return Optional.empty(); + } else { + return typeConverter.convert(object, targetType, context); + } } + } else if (typeConverter != UNCONVERTIBLE) { + return typeConverter.convert(object, targetType, context); } - return typeConverter.convert(object, targetType, context); + return Optional.empty(); } @Override @@ -117,11 +128,11 @@ public boolean canConvert(Class sourceType, Class targetType) { typeConverter = findTypeConverter(sourceType, targetType, null); if (typeConverter != null) { converterCache.put(pair, typeConverter); - return true; + return typeConverter != UNCONVERTIBLE; } return false; } - return true; + return typeConverter != UNCONVERTIBLE; } @Override @@ -829,7 +840,7 @@ protected void registerDefaultConverters() { * @return type converter */ protected TypeConverter findTypeConverter(Class sourceType, Class targetType, Class formattingAnnotation) { - TypeConverter typeConverter = null; + TypeConverter typeConverter = UNCONVERTIBLE; List sourceHierarchy = ClassUtils.resolveHierarchy(sourceType); List targetHierarchy = ClassUtils.resolveHierarchy(targetType); boolean hasFormatting = formattingAnnotation != null; @@ -860,7 +871,7 @@ protected TypeConverter findTypeConverter(Class sourceType, Class targ private SimpleDateFormat resolveFormat(ConversionContext context) { AnnotationMetadata annotationMetadata = context.getAnnotationMetadata(); - Optional format = annotationMetadata.getValue(Format.class, String.class); + Optional format = annotationMetadata.stringValue(Format.class); return format .map((pattern) -> new SimpleDateFormat(pattern, context.getLocale())) .orElse(new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", context.getLocale())); diff --git a/core/src/main/java/io/micronaut/core/util/StringUtils.java b/core/src/main/java/io/micronaut/core/util/StringUtils.java index 0f1e23713ca..708eb67d080 100644 --- a/core/src/main/java/io/micronaut/core/util/StringUtils.java +++ b/core/src/main/java/io/micronaut/core/util/StringUtils.java @@ -291,4 +291,24 @@ public static String trimToNull(@Nullable String string) { .filter(StringUtils::isNotEmpty) .orElse(null); } + + /** + * Is the boolean string true. Values that represent true are: yes, y, on, and true. + * @param booleanString The boolean string + * @return True if it is a valid value + */ + public static boolean isTrue(String booleanString) { + if (booleanString == null) { + return false; + } + switch (booleanString) { + case "yes": + case "y": + case "on": + case "true": + return true; + default: + return false; + } + } } diff --git a/core/src/test/groovy/io/micronaut/core/annotation/AnnotationValueSpec.groovy b/core/src/test/groovy/io/micronaut/core/annotation/AnnotationValueSpec.groovy new file mode 100644 index 00000000000..550b0295384 --- /dev/null +++ b/core/src/test/groovy/io/micronaut/core/annotation/AnnotationValueSpec.groovy @@ -0,0 +1,122 @@ +package io.micronaut.core.annotation + +import spock.lang.Specification + +class AnnotationValueSpec extends Specification { + + void "test class value"() { + given: + def av = AnnotationValue.builder("test.Foo") + .value(AnnotationValueSpec) + .build() + + expect: + av.classValue().get() == AnnotationValueSpec + av.classValue("value").get() == AnnotationValueSpec + av.classValue("value", Specification).get() == AnnotationValueSpec + !av.classValue("value", URL).isPresent() + } + + void "test class value 2"() { + given: + def av = AnnotationValue.builder("test.Foo") + .values(new AnnotationClassValue(AnnotationValueSpec), new AnnotationClassValue(Specification)) + .build() + + expect: + av.classValues().length == 2 + av.classValues()[0] == AnnotationValueSpec + av.classValues()[1] == Specification + av.classValue().get() == AnnotationValueSpec + av.classValue("value").get() == AnnotationValueSpec + av.classValue("value", Specification).get() == AnnotationValueSpec + !av.classValue("value", URL).isPresent() + } + + void "test INT value"() { + given: + def av = AnnotationValue.builder("test.Foo") + .value(10) + .build() + + expect: + av.intValue().asInt == 10 + } + + void "test LONG value"() { + given: + def av = AnnotationValue.builder("test.Foo") + .value(10) + .member("str", "10") + .build() + + expect: + av.longValue().asLong == 10 + av.longValue("str").asLong == 10 + } + + void "test string value"() { + given: + def av = AnnotationValue.builder("test.Foo") + .member("number", 10) + .member("bool", true) + .member("type", new AnnotationClassValue(Specification)) + .member("types", new AnnotationClassValue(Specification), new AnnotationClassValue(AnnotationValueSpec)) + .build() + + expect: + av.stringValue("number").get() == "10" + av.stringValue("bool").get() == "true" + av.stringValue("type").get() == Specification.name + av.stringValue("types").get() == Specification.name + av.stringValues("number") == ["10"] as String[] + } + + + void "test INT value array"() { + given: + int[] ints = [10, 20] + def av = AnnotationValue.builder("test.Foo") + .values(ints) + .build() + + expect: + av.intValue().asInt == 10 + } + + void "test INT value strings"() { + given: + def av = AnnotationValue.builder("test.Foo") + .values("10", "two") + .build() + + expect: + av.intValue().asInt == 10 + } + + void "test is true"() { + given: + def av = AnnotationValue.builder("test.Foo") + .member("one", "y") + .member("two", "true") + .member("three", true) + .member("four", false) + .member("five", "false") + .build() + + expect: + av.isPresent("one") + av.isTrue("one") + av.isTrue("two") + av.isTrue("three") + !av.isFalse("one") + !av.isFalse("two") + !av.isFalse("three") + !av.isTrue("four") + !av.isTrue("five") + !av.isTrue("six") + av.isFalse("four") + av.isFalse("five") + av.isFalse("six") + } +} diff --git a/function-client/src/main/java/io/micronaut/function/client/aop/FunctionClientAdvice.java b/function-client/src/main/java/io/micronaut/function/client/aop/FunctionClientAdvice.java index 7f121ca297c..21b86376ecd 100644 --- a/function-client/src/main/java/io/micronaut/function/client/aop/FunctionClientAdvice.java +++ b/function-client/src/main/java/io/micronaut/function/client/aop/FunctionClientAdvice.java @@ -73,7 +73,7 @@ public Object intercept(MethodInvocationContext context) { body = parameterValueMap; } - String functionName = context.getValue(Named.class, String.class) + String functionName = context.stringValue(Named.class) .orElse(NameUtils.hyphenate(context.getMethodName(), true)); Flowable functionDefinition = Flowable.fromPublisher(discoveryClient.getFunction(functionName)); diff --git a/function-web/src/main/java/io/micronaut/function/web/AnnotatedFunctionRouteBuilder.java b/function-web/src/main/java/io/micronaut/function/web/AnnotatedFunctionRouteBuilder.java index 380f8ca2790..44c4480fd9f 100644 --- a/function-web/src/main/java/io/micronaut/function/web/AnnotatedFunctionRouteBuilder.java +++ b/function-web/src/main/java/io/micronaut/function/web/AnnotatedFunctionRouteBuilder.java @@ -91,16 +91,12 @@ public void process(BeanDefinition beanDefinition, ExecutableMethod met if (beanDefinition.hasAnnotation(FunctionBean.class)) { String methodName = method.getMethodName(); Class declaringType = method.getDeclaringType(); - String functionName = beanDefinition.getValue(FunctionBean.class, String.class).orElse(methodName); - String functionMethod = beanDefinition.getValue(FunctionBean.class, "method", String.class).orElse(null); + String functionName = beanDefinition.stringValue(FunctionBean.class).orElse(methodName); + String functionMethod = beanDefinition.stringValue(FunctionBean.class, "method").orElse(null); UriRoute route = null; - MediaType[] consumes = method.getValue(Consumes.class, String[].class).map((types) -> - Arrays.stream(types).map(MediaType::new).toArray(MediaType[]::new) - ).orElse(null); - MediaType[] produces = method.getValue(Produces.class, String[].class).map((types) -> - Arrays.stream(types).map(MediaType::new).toArray(MediaType[]::new) - ).orElse(null); + MediaType[] consumes = Arrays.stream(method.stringValues(Consumes.class)).map(MediaType::new).toArray(MediaType[]::new); + MediaType[] produces = Arrays.stream(method.stringValues(Produces.class)).map(MediaType::new).toArray(MediaType[]::new); if (Stream.of(java.util.function.Function.class, Consumer.class, BiFunction.class, BiConsumer.class).anyMatch(type -> type.isAssignableFrom(declaringType))) { if (methodName.equals("accept") || methodName.equals("apply") || methodName.equals(functionMethod)) { diff --git a/function/src/main/java/io/micronaut/function/DefaultLocalFunctionRegistry.java b/function/src/main/java/io/micronaut/function/DefaultLocalFunctionRegistry.java index c1696f8c745..d22d89631da 100644 --- a/function/src/main/java/io/micronaut/function/DefaultLocalFunctionRegistry.java +++ b/function/src/main/java/io/micronaut/function/DefaultLocalFunctionRegistry.java @@ -149,7 +149,7 @@ public Optional, R>> findBiFuncti public void process(BeanDefinition beanDefinition, ExecutableMethod method) { if (method.hasAnnotation(FunctionBean.class)) { - String functionId = method.getValue(FunctionBean.class, String.class).orElse(null); + String functionId = method.stringValue(FunctionBean.class).orElse(null); Class declaringType = method.getDeclaringType(); if (StringUtils.isEmpty(functionId)) { String typeName = declaringType.getSimpleName(); diff --git a/http-client/src/main/java/io/micronaut/http/client/DefaultHttpClient.java b/http-client/src/main/java/io/micronaut/http/client/DefaultHttpClient.java index 674e1259529..364fcd0a32e 100644 --- a/http-client/src/main/java/io/micronaut/http/client/DefaultHttpClient.java +++ b/http-client/src/main/java/io/micronaut/http/client/DefaultHttpClient.java @@ -715,7 +715,7 @@ public Flowable connect(Class clientEndpointType @Override public Flowable connect(Class clientEndpointType, Map parameters) { WebSocketBean webSocketBean = webSocketRegistry.getWebSocket(clientEndpointType); - String uri = webSocketBean.getBeanDefinition().getValue(ClientWebSocket.class, String.class).orElse("/ws"); + String uri = webSocketBean.getBeanDefinition().stringValue(ClientWebSocket.class).orElse("/ws"); uri = UriTemplate.of(uri).expand(parameters); MutableHttpRequest request = io.micronaut.http.HttpRequest.GET(uri); Publisher uriPublisher = resolveRequestURI(request); diff --git a/http-client/src/main/java/io/micronaut/http/client/interceptor/HttpClientIntroductionAdvice.java b/http-client/src/main/java/io/micronaut/http/client/interceptor/HttpClientIntroductionAdvice.java index c09223075f5..707493b2bce 100644 --- a/http-client/src/main/java/io/micronaut/http/client/interceptor/HttpClientIntroductionAdvice.java +++ b/http-client/src/main/java/io/micronaut/http/client/interceptor/HttpClientIntroductionAdvice.java @@ -266,7 +266,7 @@ public Object intercept(MethodInvocationContext context) { body = definedValue; } else if (annotationMetadata.isAnnotationPresent(Header.class)) { - String headerName = annotationMetadata.getValue(Header.class, String.class).orElse(null); + String headerName = annotationMetadata.stringValue(Header.class).orElse(null); if (StringUtils.isEmpty(headerName)) { headerName = NameUtils.hyphenate(argumentName); } @@ -274,7 +274,7 @@ public Object intercept(MethodInvocationContext context) { conversionService.convert(definedValue, String.class) .ifPresent(o -> headers.put(finalHeaderName, o)); } else if (annotationMetadata.isAnnotationPresent(CookieValue.class)) { - String cookieName = annotationMetadata.getValue(CookieValue.class, String.class).orElse(null); + String cookieName = annotationMetadata.stringValue(CookieValue.class).orElse(null); if (StringUtils.isEmpty(cookieName)) { cookieName = argumentName; } @@ -284,7 +284,7 @@ public Object intercept(MethodInvocationContext context) { .ifPresent(o -> cookies.add(new NettyCookie(finalCookieName, o))); } else if (annotationMetadata.isAnnotationPresent(QueryValue.class)) { - String parameterName = annotationMetadata.getValue(QueryValue.class, String.class).orElse(null); + String parameterName = annotationMetadata.stringValue(QueryValue.class).orElse(null); conversionService.convert(definedValue, ConversionContext.of(String.class).with(annotationMetadata)).ifPresent(o -> { if (!StringUtils.isEmpty(parameterName)) { paramMap.put(parameterName, o); @@ -294,7 +294,7 @@ public Object intercept(MethodInvocationContext context) { } }); } else if (annotationMetadata.isAnnotationPresent(RequestAttribute.class)) { - String attributeName = annotationMetadata.getValue(Annotation.class, String.class).orElse(null); + String attributeName = annotationMetadata.stringValue(RequestAttribute.class).orElse(null); if (StringUtils.isEmpty(attributeName)) { attributeName = NameUtils.hyphenate(argumentName); } @@ -302,7 +302,7 @@ public Object intercept(MethodInvocationContext context) { conversionService.convert(definedValue, Object.class) .ifPresent(o -> attributes.put(finalAttributeName, o)); } else if (annotationMetadata.isAnnotationPresent(PathVariable.class)) { - String parameterName = annotationMetadata.getValue(PathVariable.class, String.class).orElse(null); + String parameterName = annotationMetadata.stringValue(PathVariable.class).orElse(null); conversionService.convert(definedValue, ConversionContext.of(String.class).with(annotationMetadata)).ifPresent(o -> { if (!StringUtils.isEmpty(o)) { paramMap.put(parameterName, o); @@ -351,7 +351,10 @@ public Object intercept(MethodInvocationContext context) { if (body != null) { request.body(body); - MediaType[] contentTypes = context.getValue(Produces.class, MediaType[].class).orElse(DEFAULT_ACCEPT_TYPES); + MediaType[] contentTypes = MediaType.of(context.stringValues(Produces.class)); + if (ArrayUtils.isEmpty(contentTypes)) { + contentTypes = DEFAULT_ACCEPT_TYPES; + } if (ArrayUtils.isNotEmpty(contentTypes)) { request.contentType(contentTypes[0]); } @@ -378,7 +381,10 @@ public Object intercept(MethodInvocationContext context) { } } - MediaType[] acceptTypes = context.getValue(Consumes.class, MediaType[].class).orElse(DEFAULT_ACCEPT_TYPES); + MediaType[] acceptTypes = MediaType.of(context.stringValues(Consumes.class)); + if (ArrayUtils.isEmpty(acceptTypes)) { + acceptTypes = DEFAULT_ACCEPT_TYPES; + } boolean isFuture = CompletableFuture.class.isAssignableFrom(javaReturnType); final Class methodDeclaringType = declaringType; diff --git a/http-client/src/main/java/io/micronaut/http/client/websocket/NettyWebSocketClientHandler.java b/http-client/src/main/java/io/micronaut/http/client/websocket/NettyWebSocketClientHandler.java index f91b39a134f..f85c88f4263 100644 --- a/http-client/src/main/java/io/micronaut/http/client/websocket/NettyWebSocketClientHandler.java +++ b/http-client/src/main/java/io/micronaut/http/client/websocket/NettyWebSocketClientHandler.java @@ -101,7 +101,7 @@ public NettyWebSocketClientHandler( this.originatingRequest = request; this.emitter = emitter; this.webSocketStateBinderRegistry = new WebSocketStateBinderRegistry(requestBinderRegistry != null ? requestBinderRegistry : new DefaultRequestBinderRegistry(ConversionService.SHARED)); - String clientPath = webSocketBean.getBeanDefinition().getValue(ClientWebSocket.class, String.class).orElse(""); + String clientPath = webSocketBean.getBeanDefinition().stringValue(ClientWebSocket.class).orElse(""); UriMatchTemplate matchTemplate = UriMatchTemplate.of(clientPath); this.matchInfo = matchTemplate.match(request.getPath()).orElse(null); } diff --git a/http-server/src/main/java/io/micronaut/http/server/websocket/ServerWebSocketProcessor.java b/http-server/src/main/java/io/micronaut/http/server/websocket/ServerWebSocketProcessor.java index 26871a8a4a1..b51380b4dce 100644 --- a/http-server/src/main/java/io/micronaut/http/server/websocket/ServerWebSocketProcessor.java +++ b/http-server/src/main/java/io/micronaut/http/server/websocket/ServerWebSocketProcessor.java @@ -63,7 +63,7 @@ public void process(BeanDefinition beanDefinition, ExecutableMethod met if (method.isAnnotationPresent(OnMessage.class) || method.isAnnotationPresent(OnOpen.class)) { mappedWebSockets.add(beanType); - String uri = beanDefinition.getValue(ServerWebSocket.class, String.class).orElse("/ws"); + String uri = beanDefinition.stringValue(ServerWebSocket.class).orElse("/ws"); UriRoute route = GET(uri, method); diff --git a/http/src/main/java/io/micronaut/http/bind/binders/CookieAnnotationBinder.java b/http/src/main/java/io/micronaut/http/bind/binders/CookieAnnotationBinder.java index fbe8e6ee125..26b36d8963b 100644 --- a/http/src/main/java/io/micronaut/http/bind/binders/CookieAnnotationBinder.java +++ b/http/src/main/java/io/micronaut/http/bind/binders/CookieAnnotationBinder.java @@ -51,7 +51,7 @@ public Class getAnnotationType() { public BindingResult bind(ArgumentConversionContext argument, HttpRequest source) { ConvertibleValues parameters = source.getCookies(); AnnotationMetadata annotationMetadata = argument.getAnnotationMetadata(); - String parameterName = annotationMetadata.getValue(CookieValue.class, String.class) + String parameterName = annotationMetadata.stringValue(CookieValue.class) .orElse(argument.getArgument().getName()); return doBind(argument, parameters, parameterName); } diff --git a/http/src/main/java/io/micronaut/http/bind/binders/HeaderAnnotationBinder.java b/http/src/main/java/io/micronaut/http/bind/binders/HeaderAnnotationBinder.java index 923ebb61251..41c942e39f1 100644 --- a/http/src/main/java/io/micronaut/http/bind/binders/HeaderAnnotationBinder.java +++ b/http/src/main/java/io/micronaut/http/bind/binders/HeaderAnnotationBinder.java @@ -47,7 +47,7 @@ public HeaderAnnotationBinder(ConversionService conversionService) { public BindingResult bind(ArgumentConversionContext argument, HttpRequest source) { ConvertibleMultiValues parameters = source.getHeaders(); AnnotationMetadata annotationMetadata = argument.getAnnotationMetadata(); - String parameterName = annotationMetadata.getValue(Header.class, String.class).orElse(argument.getArgument().getName()); + String parameterName = annotationMetadata.stringValue(Header.class).orElse(argument.getArgument().getName()); return doBind(argument, parameters, parameterName); } diff --git a/http/src/main/java/io/micronaut/http/bind/binders/ParameterAnnotationBinder.java b/http/src/main/java/io/micronaut/http/bind/binders/ParameterAnnotationBinder.java index 12a5add764b..eca51d40454 100644 --- a/http/src/main/java/io/micronaut/http/bind/binders/ParameterAnnotationBinder.java +++ b/http/src/main/java/io/micronaut/http/bind/binders/ParameterAnnotationBinder.java @@ -64,7 +64,7 @@ public BindingResult bind(ArgumentConversionContext context, HttpRequest bind(ArgumentConversionContext context, HttpRequest getAnnotationType() { public BindingResult bind(ArgumentConversionContext argument, HttpRequest source) { MutableConvertibleValues parameters = source.getAttributes(); AnnotationMetadata annotationMetadata = argument.getAnnotationMetadata(); - String parameterName = annotationMetadata.getValue(RequestAttribute.class, String.class).orElse(argument.getArgument().getName()); + String parameterName = annotationMetadata.stringValue(RequestAttribute.class).orElse(argument.getArgument().getName()); return doBind(argument, parameters, parameterName); } diff --git a/inject-groovy/src/main/groovy/io/micronaut/ast/groovy/config/GroovyConfigurationMetadataBuilder.groovy b/inject-groovy/src/main/groovy/io/micronaut/ast/groovy/config/GroovyConfigurationMetadataBuilder.groovy index 458f52dca58..73e32788647 100644 --- a/inject-groovy/src/main/groovy/io/micronaut/ast/groovy/config/GroovyConfigurationMetadataBuilder.groovy +++ b/inject-groovy/src/main/groovy/io/micronaut/ast/groovy/config/GroovyConfigurationMetadataBuilder.groovy @@ -85,10 +85,10 @@ class GroovyConfigurationMetadataBuilder extends ConfigurationMetadataBuilder AnnotationMetadata ownerMetadata = AstAnnotationUtils.getAnnotationMetadata(sourceUnit, owningType) - return ownerMetadata.getValue(ConfigurationReader.class, String.class) + return ownerMetadata.stringValue(ConfigurationReader.class) .map(pathEvaluationFunction(ownerMetadata)).orElseThrow({ -> new IllegalStateException("Non @ConfigurationProperties type visited") }) @@ -122,7 +122,7 @@ class GroovyConfigurationMetadataBuilder extends ConfigurationMetadataBuilder parentConfig = AstAnnotationUtils.getAnnotationMetadata(sourceUnit, superclass).getValue(ConfigurationReader.class, String.class) + Optional parentConfig = AstAnnotationUtils.getAnnotationMetadata(sourceUnit, superclass).stringValue(ConfigurationReader.class) if (parentConfig.isPresent()) { path.insert(0, parentConfig.get() + '.') superclass = superclass.getSuperClass() diff --git a/inject/src/main/java/io/micronaut/context/AbstractBeanDefinition.java b/inject/src/main/java/io/micronaut/context/AbstractBeanDefinition.java index fd5f0557012..fc7752b412e 100644 --- a/inject/src/main/java/io/micronaut/context/AbstractBeanDefinition.java +++ b/inject/src/main/java/io/micronaut/context/AbstractBeanDefinition.java @@ -747,7 +747,7 @@ protected final Object getValueForMethodArgument(BeanResolutionContext resolutio if (context instanceof ApplicationContext) { // can't use orElseThrow here due to compiler bug try { - String valueAnnStr = argument.getAnnotationMetadata().getValue(Value.class, String.class).orElse(null); + String valueAnnStr = argument.getAnnotationMetadata().stringValue(Value.class).orElse(null); Class argumentType = argument.getType(); if (isInnerConfiguration(argumentType)) { @@ -799,7 +799,7 @@ protected final boolean containsValueForMethodArgument(BeanResolutionContext res if (context instanceof ApplicationContext) { MethodInjectionPoint injectionPoint = methodInjectionPoints.get(methodIndex); Argument argument = injectionPoint.getArguments()[argIndex]; - String valueAnnStr = argument.getAnnotationMetadata().getValue(Value.class, String.class).orElse(null); + String valueAnnStr = argument.getAnnotationMetadata().stringValue(Value.class).orElse(null); String valString = resolvePropertyValueName(resolutionContext, injectionPoint.getAnnotationMetadata(), argument, valueAnnStr); ApplicationContext applicationContext = (ApplicationContext) context; Class type = argument.getType(); @@ -1016,7 +1016,7 @@ protected final Object getValueForConstructorArgument(BeanResolutionContext reso if (context instanceof ApplicationContext) { ApplicationContext propertyResolver = (ApplicationContext) context; AnnotationMetadata argMetadata = argument.getAnnotationMetadata(); - Optional valAnn = argMetadata.getValue(Value.class, String.class); + Optional valAnn = argMetadata.stringValue(Value.class); String prop = valAnn.orElseGet(() -> argMetadata.getValue(Property.class, "name", String.class) .orElseThrow(() -> new IllegalStateException("Compiled getValueForMethodArgument(..) call present but @Value annotation missing.")) @@ -1168,7 +1168,7 @@ protected final Object getValueForField(BeanResolutionContext resolutionContext, try { if (context instanceof PropertyResolver) { final AnnotationMetadata annotationMetadata = injectionPoint.getAnnotationMetadata(); - String valueAnnVal = annotationMetadata.getValue(Value.class, String.class).orElse(null); + String valueAnnVal = annotationMetadata.stringValue(Value.class).orElse(null); Class fieldType = injectionPoint.getType(); if (isInnerConfiguration(fieldType)) { Qualifier qualifier = resolveQualifier(resolutionContext, injectionPoint.asArgument(), true); @@ -1242,7 +1242,7 @@ protected final boolean containsValueForField(BeanResolutionContext resolutionCo if (context instanceof ApplicationContext) { FieldInjectionPoint injectionPoint = fieldInjectionPoints.get(fieldIndex); final AnnotationMetadata annotationMetadata = injectionPoint.getAnnotationMetadata(); - String valueAnnVal = annotationMetadata.getValue(Value.class, String.class).orElse(null); + String valueAnnVal = annotationMetadata.stringValue(Value.class).orElse(null); String valString = resolvePropertyValueName(resolutionContext, injectionPoint, valueAnnVal, annotationMetadata); ApplicationContext applicationContext = (ApplicationContext) context; Class fieldType = injectionPoint.getType(); diff --git a/inject/src/main/java/io/micronaut/context/DefaultApplicationContext.java b/inject/src/main/java/io/micronaut/context/DefaultApplicationContext.java index 61b7c26c4ac..e60e2c7cb4a 100644 --- a/inject/src/main/java/io/micronaut/context/DefaultApplicationContext.java +++ b/inject/src/main/java/io/micronaut/context/DefaultApplicationContext.java @@ -245,8 +245,8 @@ protected Collection> findBeanCandidates(Class beanType for (BeanDefinition candidate : candidates) { if (candidate.hasDeclaredStereotype(EachProperty.class)) { - String property = candidate.getValue(EachProperty.class, String.class).orElse(null); - String primaryPrefix = candidate.getValue(EachProperty.class, "primary", String.class).orElse(null); + String property = candidate.stringValue(EachProperty.class).orElse(null); + String primaryPrefix = candidate.stringValue(EachProperty.class, "primary").orElse(null); if (StringUtils.isNotEmpty(property)) { Map entries = getProperty(property, Map.class, Collections.emptyMap()); @@ -268,7 +268,7 @@ protected Collection> findBeanCandidates(Class beanType throw new IllegalArgumentException("Blank value specified to @Each property for bean: " + candidate); } } else if (candidate.hasDeclaredStereotype(EachBean.class)) { - Class dependentType = candidate.getValue(EachBean.class, Class.class).orElse(null); + Class dependentType = candidate.classValue(EachBean.class).orElse(null); if (dependentType == null) { transformedCandidates.add(candidate); continue; diff --git a/inject/src/main/java/io/micronaut/context/DefaultBeanContext.java b/inject/src/main/java/io/micronaut/context/DefaultBeanContext.java index ae0dac48ff4..7986754e371 100644 --- a/inject/src/main/java/io/micronaut/context/DefaultBeanContext.java +++ b/inject/src/main/java/io/micronaut/context/DefaultBeanContext.java @@ -2254,7 +2254,7 @@ private Qualifier resolveDynamicQualifier(BeanDefinition beanDefinitio } } if (qualifier == null) { - Optional optional = beanDefinition.getValue(javax.inject.Named.class, String.class); + Optional optional = beanDefinition.stringValue(javax.inject.Named.class); qualifier = (Qualifier) optional.map(name -> Qualifiers.byAnnotation(beanDefinition, name)).orElse(null); } return qualifier; diff --git a/inject/src/main/java/io/micronaut/context/DefaultConstructorInjectionPoint.java b/inject/src/main/java/io/micronaut/context/DefaultConstructorInjectionPoint.java index 89d7e2d3fec..4d0949ca401 100644 --- a/inject/src/main/java/io/micronaut/context/DefaultConstructorInjectionPoint.java +++ b/inject/src/main/java/io/micronaut/context/DefaultConstructorInjectionPoint.java @@ -137,6 +137,7 @@ public String toString() { * Internal environment aware annotation metadata delegate. */ private final class ConstructorAnnotationMetadata extends AbstractEnvironmentAnnotationMetadata { + ConstructorAnnotationMetadata(DefaultAnnotationMetadata targetMetadata) { super(targetMetadata); } diff --git a/inject/src/main/java/io/micronaut/inject/annotation/AbstractAnnotationMetadataBuilder.java b/inject/src/main/java/io/micronaut/inject/annotation/AbstractAnnotationMetadataBuilder.java index 3784e4be6f4..2fb5e066362 100644 --- a/inject/src/main/java/io/micronaut/inject/annotation/AbstractAnnotationMetadataBuilder.java +++ b/inject/src/main/java/io/micronaut/inject/annotation/AbstractAnnotationMetadataBuilder.java @@ -636,7 +636,7 @@ private AnnotationMetadata buildInternal(T parent, T element, DefaultAnnotationM } if (!annotationMetadata.hasDeclaredStereotype(Scope.class) && annotationMetadata.hasDeclaredStereotype(DefaultScope.class)) { - Optional value = annotationMetadata.getValue(DefaultScope.class, String.class); + Optional value = annotationMetadata.stringValue(DefaultScope.class); value.ifPresent(name -> annotationMetadata.addDeclaredAnnotation(name, Collections.emptyMap())); } return annotationMetadata; diff --git a/inject/src/main/java/io/micronaut/inject/annotation/AbstractEnvironmentAnnotationMetadata.java b/inject/src/main/java/io/micronaut/inject/annotation/AbstractEnvironmentAnnotationMetadata.java index 9e06330cbe8..a831da380d9 100644 --- a/inject/src/main/java/io/micronaut/inject/annotation/AbstractEnvironmentAnnotationMetadata.java +++ b/inject/src/main/java/io/micronaut/inject/annotation/AbstractEnvironmentAnnotationMetadata.java @@ -28,10 +28,8 @@ import javax.annotation.Nullable; import java.lang.annotation.Annotation; import java.lang.reflect.Array; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; +import java.util.*; +import java.util.function.Function; import java.util.stream.Collectors; /** @@ -55,6 +53,129 @@ protected AbstractEnvironmentAnnotationMetadata(DefaultAnnotationMetadata target this.annotationMetadata = targetMetadata; } + @Override + public boolean isTrue(@Nonnull String annotation, @Nonnull String member) { + Environment environment = getEnvironment(); + if (environment != null) { + return annotationMetadata.isTrue(annotation, member, o -> { + if (o instanceof String) { + String v = (String) o; + if (v.contains("${")) { + return environment.getPlaceholderResolver().resolveRequiredPlaceholders(v); + } + } + return o; + }); + } else { + return annotationMetadata.isTrue(annotation, member); + } + } + + @Override + public boolean isFalse(@Nonnull String annotation, @Nonnull String member) { + Environment environment = getEnvironment(); + if (environment != null) { + return !annotationMetadata.isTrue(annotation, member, o -> { + if (o instanceof String) { + String v = (String) o; + if (v.contains("${")) { + return environment.getPlaceholderResolver().resolveRequiredPlaceholders(v); + } + } + return o; + }); + } else { + return !annotationMetadata.isTrue(annotation, member); + } + } + + @Nonnull + @Override + public Optional> getAnnotationTypeByStereotype(@Nonnull Class stereotype) { + return annotationMetadata.getAnnotationTypeByStereotype(stereotype); + } + + @Nonnull + @Override + public Optional> getAnnotationTypeByStereotype(@Nullable String stereotype) { + return annotationMetadata.getAnnotationTypeByStereotype(stereotype); + } + + @Nonnull + @Override + public Optional classValue(@Nonnull String annotation, @Nonnull String member) { + Function valueMapper = getEnvironmentValueMapper(); + return annotationMetadata.classValue(annotation, member, valueMapper); + } + + @Nonnull + @Override + public Optional classValue(@Nonnull Class annotation, @Nonnull String member) { + Function valueMapper = getEnvironmentValueMapper(); + return annotationMetadata.classValue(annotation, member, valueMapper); + } + + @Nonnull + @Override + public Optional stringValue(@Nonnull Class annotation, @Nonnull String member) { + Function valueMapper = getEnvironmentValueMapper(); + return annotationMetadata.stringValue(annotation, member, valueMapper); + } + + @Nonnull + @Override + public String[] stringValues(@Nonnull Class annotation, @Nonnull String member) { + Function valueMapper = getEnvironmentValueMapper(); + return annotationMetadata.stringValues(annotation, member, valueMapper); + } + + @Nonnull + @Override + public Optional stringValue(@Nonnull String annotation, @Nonnull String member) { + Function valueMapper = getEnvironmentValueMapper(); + return annotationMetadata.stringValue(annotation, member, valueMapper); + } + + @Nonnull + @Override + public OptionalInt intValue(@Nonnull String annotation, @Nonnull String member) { + Function valueMapper = getEnvironmentValueMapper(); + return annotationMetadata.intValue(annotation, member, valueMapper); + } + + @Nonnull + @Override + public OptionalInt intValue(@Nonnull Class annotation, @Nonnull String member) { + Function valueMapper = getEnvironmentValueMapper(); + return annotationMetadata.intValue(annotation, member, valueMapper); + } + + @Nonnull + @Override + public OptionalDouble doubleValue(@Nonnull String annotation, @Nonnull String member) { + Function valueMapper = getEnvironmentValueMapper(); + return annotationMetadata.doubleValue(annotation, member, valueMapper); + } + + @Nonnull + @Override + public OptionalDouble doubleValue(@Nonnull Class annotation, @Nonnull String member) { + Function valueMapper = getEnvironmentValueMapper(); + return annotationMetadata.doubleValue(annotation, member, valueMapper); + } + + @Override + public boolean isTrue(@Nonnull Class annotation, @Nonnull String member) { + Function valueMapper = getEnvironmentValueMapper(); + return annotationMetadata.isTrue(annotation, member, valueMapper); + } + + @Override + public boolean isFalse(@Nonnull Class annotation, @Nonnull String member) { + Function valueMapper = getEnvironmentValueMapper(); + return !annotationMetadata.isTrue(annotation, member, valueMapper); + } + @Override public @Nonnull Optional> getAnnotationType(@Nonnull String name) { ArgumentUtils.requireNonNull("name", name); @@ -68,7 +189,7 @@ protected AbstractEnvironmentAnnotationMetadata(DefaultAnnotationMetadata target List> values = annotationMetadata.getAnnotationValuesByType(annotationType); if (environment != null) { return values.stream().map(entries -> - new EnvironmentAnnotationValue(environment, entries) + new EnvironmentAnnotationValue<>(environment, entries) ).collect(Collectors.toList()); } return values; @@ -80,7 +201,7 @@ protected AbstractEnvironmentAnnotationMetadata(DefaultAnnotationMetadata target Environment environment = getEnvironment(); List> values = annotationMetadata.getDeclaredAnnotationValuesByType(annotationType); if (environment != null) { - return values.stream().map(entries -> new EnvironmentAnnotationValue(environment, entries)) + return values.stream().map(entries -> new EnvironmentAnnotationValue<>(environment, entries)) .collect(Collectors.toList()); } return values; @@ -221,12 +342,31 @@ public boolean hasDeclaredStereotype(@Nullable String annotation) { protected void addValuesToResults(List results, AnnotationValue values) { Environment environment = getEnvironment(); if (environment != null) { - results.add(new EnvironmentAnnotationValue(environment, values)); + results.add(new EnvironmentAnnotationValue<>(environment, values)); } else { results.add(values); } } + /** + * @return The value mapper for the environment + */ + private @Nullable Function getEnvironmentValueMapper() { + Environment env = getEnvironment(); + if (env != null) { + return o -> { + if (o instanceof String) { + String v = (String) o; + if (v.contains("${")) { + return env.getPlaceholderResolver().resolveRequiredPlaceholders(v); + } + } + return o; + }; + } + return null; + } + private OptionalValues resolveOptionalValuesForEnvironment(String annotation, Class valueType, Map> allAnnotations, Map> allStereotypes, Environment environment) { if (allAnnotations != null && StringUtils.isNotEmpty(annotation)) { Map values = allAnnotations.get(annotation); diff --git a/inject/src/main/java/io/micronaut/inject/annotation/DefaultAnnotationMetadata.java b/inject/src/main/java/io/micronaut/inject/annotation/DefaultAnnotationMetadata.java index bc0b2e2725b..7a121f9d8e4 100644 --- a/inject/src/main/java/io/micronaut/inject/annotation/DefaultAnnotationMetadata.java +++ b/inject/src/main/java/io/micronaut/inject/annotation/DefaultAnnotationMetadata.java @@ -34,6 +34,7 @@ import java.lang.reflect.Array; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; /** * Default implementation of {@link AnnotationMetadata}. @@ -76,12 +77,18 @@ public class DefaultAnnotationMetadata extends AbstractAnnotationMetadata implem }); } - @Nullable Map> declaredAnnotations; - @Nullable Map> allAnnotations; - @Nullable Map> declaredStereotypes; - @Nullable Map> allStereotypes; - @Nullable Map> annotationsByStereotype; - @Nullable Map> annotationDefaultValues; + @Nullable + Map> declaredAnnotations; + @Nullable + Map> allAnnotations; + @Nullable + Map> declaredStereotypes; + @Nullable + Map> allStereotypes; + @Nullable + Map> annotationsByStereotype; + @Nullable + Map> annotationDefaultValues; private Map annotationValuesByType = new ConcurrentHashMap<>(2); // should not be used in any of the read methods @@ -107,11 +114,11 @@ protected DefaultAnnotationMetadata() { @Internal @UsedByGeneratedCode public DefaultAnnotationMetadata( - @Nullable Map> declaredAnnotations, - @Nullable Map> declaredStereotypes, - @Nullable Map> allStereotypes, - @Nullable Map> allAnnotations, - @Nullable Map> annotationsByStereotype) { + @Nullable Map> declaredAnnotations, + @Nullable Map> declaredStereotypes, + @Nullable Map> allStereotypes, + @Nullable Map> allAnnotations, + @Nullable Map> annotationsByStereotype) { super(declaredAnnotations, allAnnotations); this.declaredAnnotations = declaredAnnotations; this.declaredStereotypes = declaredStereotypes; @@ -128,7 +135,337 @@ public DefaultAnnotationMetadata( } @Override - public @Nonnull Optional getValue(@Nonnull Class annotation, @Nonnull String member, @Nonnull Class requiredType) { + public boolean isPresent(@Nonnull String annotation, @Nonnull String member) { + boolean isPresent = false; + if (allAnnotations != null && StringUtils.isNotEmpty(annotation)) { + Map values = allAnnotations.get(annotation); + if (values != null) { + isPresent = values.containsKey(member); + } else if (allStereotypes != null) { + values = allStereotypes.get(annotation); + if (values != null) { + isPresent = values.containsKey(member); + } + } + } + return isPresent; + } + + @Nonnull + @Override + public Optional classValue(@Nonnull Class annotation, @Nonnull String member) { + return classValue(annotation, member, null); + } + + /** + * Retrieve the class value and optionally map its value. + * @param annotation The annotation + * @param member The member + * @param valueMapper The value mapper + * @return The class value + */ + Optional classValue(@Nonnull Class annotation, @Nonnull String member, Function valueMapper) { + ArgumentUtils.requireNonNull("annotation", annotation); + ArgumentUtils.requireNonNull("member", member); + final Repeatable repeatable = annotation.getAnnotation(Repeatable.class); + if (repeatable != null) { + Object v = getRawSingleValue(repeatable.value().getName(), VALUE_MEMBER, valueMapper); + if (v instanceof AnnotationValue) { + Optional o = ((AnnotationValue) v).classValue(member, valueMapper); + return o; + } + return Optional.empty(); + } else { + return classValue(annotation.getName(), member); + } + } + + @Nonnull + @Override + public Optional classValue(@Nonnull String annotation, @Nonnull String member) { + ArgumentUtils.requireNonNull("annotation", annotation); + ArgumentUtils.requireNonNull("member", member); + return classValue(annotation, member, null); + } + + + /** + * Retrieve the class value and optionally map its value. + * @param annotation The annotation + * @param member The member + * @param valueMapper The value mapper + * @return The class value + */ + @Internal + Optional classValue(@Nonnull String annotation, @Nonnull String member, @Nullable Function valueMapper) { + Object rawValue = getRawSingleValue(annotation, member, valueMapper); + + if (rawValue instanceof AnnotationClassValue) { + return ((AnnotationClassValue) rawValue).getType(); + } else if (rawValue instanceof Class) { + return Optional.of((Class) rawValue); + } else if (rawValue != null) { + Optional converted = ConversionService.SHARED.convert(rawValue, Class.class); + return converted; + } + return Optional.empty(); + } + + @Nonnull + @Override + public OptionalInt intValue(@Nonnull String annotation, @Nonnull String member) { + ArgumentUtils.requireNonNull("annotation", annotation); + ArgumentUtils.requireNonNull("member", member); + + return intValue(annotation, member, null); + } + + @Nonnull + @Override + public OptionalInt intValue(@Nonnull Class annotation, @Nonnull String member) { + return intValue(annotation, member, null); + } + + /** + * Retrieve the int value and optionally map its value. + * @param annotation The annotation + * @param member The member + * @param valueMapper The value mapper + * @return The int value + */ + @Internal + OptionalInt intValue(@Nonnull Class annotation, @Nonnull String member, @Nullable Function valueMapper) { + ArgumentUtils.requireNonNull("annotation", annotation); + ArgumentUtils.requireNonNull("member", member); + final Repeatable repeatable = annotation.getAnnotation(Repeatable.class); + if (repeatable != null) { + Object v = getRawSingleValue(repeatable.value().getName(), VALUE_MEMBER, valueMapper); + if (v instanceof AnnotationValue) { + return ((AnnotationValue) v).intValue(member, valueMapper); + } + return OptionalInt.empty(); + } else { + return intValue(annotation.getName(), member); + } + } + + /** + * Retrieve the int value and optionally map its value. + * @param annotation The annotation + * @param member The member + * @param valueMapper The value mapper + * @return The int value + */ + @Nonnull + OptionalInt intValue(@Nonnull String annotation, @Nonnull String member, @Nullable Function valueMapper) { + Object rawValue = getRawSingleValue(annotation, member, valueMapper); + if (rawValue instanceof Number) { + return OptionalInt.of(((Number) rawValue).intValue()); + } + return OptionalInt.empty(); + } + + @Nonnull + @Override + public Optional stringValue(@Nonnull Class annotation, @Nonnull String member) { + return stringValue(annotation, member, null); + } + + /** + * Retrieve the string value and optionally map its value. + * @param annotation The annotation + * @param member The member + * @param valueMapper The value mapper + * @return The int value + */ + Optional stringValue(@Nonnull Class annotation, @Nonnull String member, Function valueMapper) { + ArgumentUtils.requireNonNull("annotation", annotation); + final Repeatable repeatable = annotation.getAnnotation(Repeatable.class); + if (repeatable != null) { + Object v = getRawSingleValue(repeatable.value().getName(), VALUE_MEMBER, valueMapper); + if (v instanceof AnnotationValue) { + return ((AnnotationValue) v).stringValue(member, valueMapper); + } + return Optional.empty(); + } else { + return stringValue(annotation.getName(), member, valueMapper); + } + } + + @Nonnull + @Override + public String[] stringValues(@Nonnull Class annotation, @Nonnull String member) { + return stringValues(annotation, member, null); + } + + /** + * Retrieve the string value and optionally map its value. + * @param annotation The annotation + * @param member The member + * @param valueMapper The value mapper + * @return The int value + */ + @Nonnull String[] stringValues(@Nonnull Class annotation, @Nonnull String member, Function valueMapper) { + ArgumentUtils.requireNonNull("annotation", annotation); + final Repeatable repeatable = annotation.getAnnotation(Repeatable.class); + if (repeatable != null) { + Object v = getRawValue(repeatable.value().getName(), member); + if (v instanceof AnnotationValue) { + return ((AnnotationValue) v).stringValues(member, valueMapper); + } + return StringUtils.EMPTY_STRING_ARRAY; + } else { + Object v = getRawValue(annotation.getName(), member); + String[] strings = AnnotationValue.resolveStringValues(v, valueMapper); + return strings != null ? strings : StringUtils.EMPTY_STRING_ARRAY; + } + } + + @Nonnull + @Override + public Optional stringValue(@Nonnull String annotation, @Nonnull String member) { + ArgumentUtils.requireNonNull("annotation", annotation); + ArgumentUtils.requireNonNull("member", member); + return stringValue(annotation, member, null); + } + + /** + * Retrieve the string value and optionally map its value. + * @param annotation The annotation + * @param member The member + * @param valueMapper The value mapper + * @return The string value + */ + @Nonnull + Optional stringValue(@Nonnull String annotation, @Nonnull String member, @Nullable Function valueMapper) { + Object rawValue = getRawSingleValue(annotation, member, valueMapper); + if (rawValue instanceof CharSequence) { + return Optional.of(rawValue.toString()); + } else if (rawValue instanceof Class) { + String name = ((Class) rawValue).getName(); + return Optional.of(name); + } else if (rawValue != null) { + return Optional.of(rawValue.toString()); + } + return Optional.empty(); + } + + @Override + public boolean isTrue(@Nonnull Class annotation, @Nonnull String member) { + return isTrue(annotation, member, null); + } + + /** + * Retrieve the boolean value and optionally map its value. + * @param annotation The annotation + * @param member The member + * @param valueMapper The value mapper + * @return The boolean value + */ + boolean isTrue(@Nonnull Class annotation, @Nonnull String member, Function valueMapper) { + ArgumentUtils.requireNonNull("annotation", annotation); + ArgumentUtils.requireNonNull("member", member); + final Repeatable repeatable = annotation.getAnnotation(Repeatable.class); + if (repeatable != null) { + Object v = getRawSingleValue(repeatable.value().getName(), VALUE_MEMBER, valueMapper); + if (v instanceof AnnotationValue) { + return ((AnnotationValue) v).isTrue(member, valueMapper); + } + return false; + } else { + return isTrue(annotation.getName(), member, valueMapper); + } + } + + @Override + public boolean isTrue(@Nonnull String annotation, @Nonnull String member) { + ArgumentUtils.requireNonNull("annotation", annotation); + ArgumentUtils.requireNonNull("member", member); + + return isTrue(annotation, member, null); + } + + /** + * Retrieve the boolean value and optionally map its value. + * @param annotation The annotation + * @param member The member + * @param valueMapper The value mapper + * @return The boolean value + */ + boolean isTrue(@Nonnull String annotation, @Nonnull String member, @Nullable Function valueMapper) { + Object rawValue = getRawSingleValue(annotation, member, valueMapper); + + if (rawValue instanceof Boolean) { + return (Boolean) rawValue; + } else if (rawValue != null) { + String booleanString = rawValue.toString().toLowerCase(Locale.ENGLISH); + return StringUtils.isTrue(booleanString); + } + return false; + } + + @Override + public boolean isFalse(@Nonnull String annotation, @Nonnull String member) { + return !isTrue(annotation, member); + } + + @Nonnull + @Override + public OptionalDouble doubleValue(@Nonnull String annotation, @Nonnull String member) { + ArgumentUtils.requireNonNull("annotation", annotation); + ArgumentUtils.requireNonNull("member", member); + return doubleValue(annotation, member, null); + } + + @Nonnull + @Override + public OptionalDouble doubleValue(@Nonnull Class annotation, @Nonnull String member) { + return doubleValue(annotation, member, null); + } + + /** + * Retrieve the double value and optionally map its value. + * @param annotation The annotation + * @param member The member + * @param valueMapper The value mapper + * @return The double value + */ + @Internal + OptionalDouble doubleValue(@Nonnull Class annotation, @Nonnull String member, @Nullable Function valueMapper) { + ArgumentUtils.requireNonNull("annotation", annotation); + ArgumentUtils.requireNonNull("member", member); + final Repeatable repeatable = annotation.getAnnotation(Repeatable.class); + if (repeatable != null) { + Object v = getRawSingleValue(repeatable.value().getName(), VALUE_MEMBER, valueMapper); + if (v instanceof AnnotationValue) { + return ((AnnotationValue) v).doubleValue(member, valueMapper); + } + return OptionalDouble.empty(); + } else { + return doubleValue(annotation.getName(), member); + } + } + + /** + * Retrieve the double value and optionally map its value. + * @param annotation The annotation + * @param member The member + * @param valueMapper The value mapper + * @return The double value + */ + @Nonnull + @Internal + OptionalDouble doubleValue(@Nonnull String annotation, @Nonnull String member, Function valueMapper) { + Object rawValue = getRawSingleValue(annotation, member, valueMapper); + if (rawValue instanceof Number) { + return OptionalDouble.of(((Number) rawValue).doubleValue()); + } + return OptionalDouble.empty(); + } + + @Override + public @Nonnull + Optional getValue(@Nonnull Class annotation, @Nonnull String member, @Nonnull Class requiredType) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("member", member); ArgumentUtils.requireNonNull("requiredType", requiredType); @@ -148,7 +485,8 @@ public DefaultAnnotationMetadata( } @Override - public @Nonnull Optional getValue(@Nonnull String annotation, @Nonnull String member, @Nonnull Argument requiredType) { + public @Nonnull + Optional getValue(@Nonnull String annotation, @Nonnull String member, @Nonnull Argument requiredType) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("member", member); ArgumentUtils.requireNonNull("requiredType", requiredType); @@ -180,7 +518,8 @@ public DefaultAnnotationMetadata( } @Override - public @Nonnull Optional getDefaultValue(@Nonnull String annotation, @Nonnull String member, @Nonnull Class requiredType) { + public @Nonnull + Optional getDefaultValue(@Nonnull String annotation, @Nonnull String member, @Nonnull Class requiredType) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("member", member); ArgumentUtils.requireNonNull("requiredType", requiredType); @@ -194,7 +533,8 @@ public DefaultAnnotationMetadata( @SuppressWarnings("unchecked") @Override - public @Nonnull List> getAnnotationValuesByType(@Nullable Class annotationType) { + public @Nonnull + List> getAnnotationValuesByType(@Nullable Class annotationType) { if (annotationType != null) { List> results = annotationValuesByType.get(annotationType); if (results == null) { @@ -212,7 +552,8 @@ public DefaultAnnotationMetadata( } @Override - public @Nonnull List> getDeclaredAnnotationValuesByType(@Nonnull Class annotationType) { + public @Nonnull + List> getDeclaredAnnotationValuesByType(@Nonnull Class annotationType) { if (annotationType != null) { Map> sourceAnnotations = this.declaredAnnotations; Map> sourceStereotypes = this.declaredStereotypes; @@ -233,8 +574,8 @@ public T[] synthesizeAnnotationsByType(@Nonnull Class List> values = getAnnotationValuesByType(annotationClass); return values.stream() - .map(entries -> AnnotationMetadataSupport.buildAnnotation(annotationClass, entries.getConvertibleValues())) - .toArray(value -> (T[]) Array.newInstance(annotationClass, value)); + .map(entries -> AnnotationMetadataSupport.buildAnnotation(annotationClass, entries.getConvertibleValues())) + .toArray(value -> (T[]) Array.newInstance(annotationClass, value)); } //noinspection unchecked @@ -280,6 +621,46 @@ public boolean hasDeclaredStereotype(String annotation) { return hasDeclaredAnnotation(annotation) || (declaredStereotypes != null && StringUtils.isNotEmpty(annotation) && declaredStereotypes.containsKey(annotation)); } + @Nonnull + @Override + public Optional> getAnnotationTypeByStereotype(@Nullable String stereotype) { + if (stereotype != null) { + if (annotationsByStereotype != null) { + List annotations = annotationsByStereotype.get(stereotype); + if (CollectionUtils.isNotEmpty(annotations)) { + return getAnnotationType(annotations.get(0)); + } + } + if (allAnnotations != null && allAnnotations.containsKey(stereotype)) { + return getAnnotationType(stereotype); + } + if (declaredAnnotations != null && declaredAnnotations.containsKey(stereotype)) { + return getAnnotationType(stereotype); + } + } + return Optional.empty(); + } + + @Nonnull + @Override + public Optional getAnnotationNameByStereotype(@Nullable String stereotype) { + if (stereotype != null) { + if (annotationsByStereotype != null) { + List annotations = annotationsByStereotype.get(stereotype); + if (CollectionUtils.isNotEmpty(annotations)) { + return Optional.of(annotations.get(0)); + } + } + if (allAnnotations != null && allAnnotations.containsKey(stereotype)) { + return Optional.of(stereotype); + } + if (declaredAnnotations != null && declaredAnnotations.containsKey(stereotype)) { + return Optional.of(stereotype); + } + } + return Optional.empty(); + } + @Override public @Nonnull List getAnnotationNamesByStereotype(@Nullable String stereotype) { if (stereotype == null) { @@ -301,7 +682,8 @@ public boolean hasDeclaredStereotype(String annotation) { } @Override - public @Nonnull Set getAnnotationNames() { + public @Nonnull + Set getAnnotationNames() { if (allAnnotations != null) { return allAnnotations.keySet(); } @@ -309,7 +691,8 @@ public boolean hasDeclaredStereotype(String annotation) { } @Override - public @Nonnull Set getDeclaredAnnotationNames() { + public @Nonnull + Set getDeclaredAnnotationNames() { if (declaredAnnotations != null) { return declaredAnnotations.keySet(); } @@ -317,7 +700,8 @@ public boolean hasDeclaredStereotype(String annotation) { } @Override - public @Nonnull List getDeclaredAnnotationNamesByStereotype(@Nullable String stereotype) { + public @Nonnull + List getDeclaredAnnotationNamesByStereotype(@Nullable String stereotype) { if (stereotype == null) { return Collections.emptyList(); } @@ -341,13 +725,15 @@ public boolean hasDeclaredStereotype(String annotation) { } @Override - public @Nonnull Optional> getAnnotationType(@Nonnull String name) { + public @Nonnull + Optional> getAnnotationType(@Nonnull String name) { return AnnotationMetadataSupport.getAnnotationType(name); } @SuppressWarnings("Duplicates") @Override - public @Nonnull Optional> findAnnotation(@Nonnull String annotation) { + public @Nonnull + Optional> findAnnotation(@Nonnull String annotation) { ArgumentUtils.requireNonNull("annotation", annotation); if (allAnnotations != null && StringUtils.isNotEmpty(annotation)) { Map values = allAnnotations.get(annotation); @@ -365,7 +751,8 @@ public boolean hasDeclaredStereotype(String annotation) { @SuppressWarnings("Duplicates") @Override - public @Nonnull Optional> findDeclaredAnnotation(@Nonnull String annotation) { + public @Nonnull + Optional> findDeclaredAnnotation(@Nonnull String annotation) { ArgumentUtils.requireNonNull("annotation", annotation); if (declaredAnnotations != null && StringUtils.isNotEmpty(annotation)) { Map values = declaredAnnotations.get(annotation); @@ -382,7 +769,8 @@ public boolean hasDeclaredStereotype(String annotation) { } @Override - public @Nonnull OptionalValues getValues(@Nonnull String annotation, @Nonnull Class valueType) { + public @Nonnull + OptionalValues getValues(@Nonnull String annotation, @Nonnull Class valueType) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("valueType", valueType); if (allAnnotations != null && StringUtils.isNotEmpty(annotation)) { @@ -415,11 +803,11 @@ public boolean hasDeclaredStereotype(String annotation) { @Override public AnnotationMetadata clone() { return new DefaultAnnotationMetadata( - declaredAnnotations != null ? new HashMap<>(declaredAnnotations) : null, - declaredStereotypes != null ? new HashMap<>(declaredStereotypes) : null, - allStereotypes != null ? new HashMap<>(allStereotypes) : null, - allAnnotations != null ? new HashMap<>(allAnnotations) : null, - annotationsByStereotype != null ? new HashMap<>(annotationsByStereotype) : null + declaredAnnotations != null ? new HashMap<>(declaredAnnotations) : null, + declaredStereotypes != null ? new HashMap<>(declaredStereotypes) : null, + allStereotypes != null ? new HashMap<>(allStereotypes) : null, + allAnnotations != null ? new HashMap<>(allAnnotations) : null, + annotationsByStereotype != null ? new HashMap<>(annotationsByStereotype) : null ); } @@ -489,7 +877,7 @@ protected static boolean areAnnotationDefaultsRegistered(String annotation) { /** * Registers annotation default values. Used by generated byte code. DO NOT REMOVE. * - * @param annotation The annotation name + * @param annotation The annotation name * @param defaultValues The default values */ @SuppressWarnings("unused") @@ -502,7 +890,7 @@ protected static void registerAnnotationDefaults(String annotation, Map annotation) } - /** * Adds a repeatable annotation value. If a value already exists will be added * - * @param annotationName The annotation name + * @param annotationName The annotation name * @param annotationValue The annotation value */ protected final void addRepeatable(String annotationName, io.micronaut.core.annotation.AnnotationValue annotationValue) { @@ -543,8 +930,8 @@ protected final void addRepeatable(String annotationName, io.micronaut.core.anno /** * Adds a repeatable stereotype value. If a value already exists will be added * - * @param parents The parent annotations - * @param stereotype The annotation name + * @param parents The parent annotations + * @param stereotype The annotation name * @param annotationValue The annotation value */ protected void addRepeatableStereotype(List parents, String stereotype, io.micronaut.core.annotation.AnnotationValue annotationValue) { @@ -562,8 +949,8 @@ protected void addRepeatableStereotype(List parents, String stereotype, /** * Adds a repeatable declared stereotype value. If a value already exists will be added * - * @param parents The parent annotations - * @param stereotype The annotation name + * @param parents The parent annotations + * @param stereotype The annotation name * @param annotationValue The annotation value */ protected void addDeclaredRepeatableStereotype(List parents, String stereotype, io.micronaut.core.annotation.AnnotationValue annotationValue) { @@ -582,7 +969,7 @@ protected void addDeclaredRepeatableStereotype(List parents, String ster /** * Adds a repeatable annotation value. If a value already exists will be added * - * @param annotationName The annotation name + * @param annotationName The annotation name * @param annotationValue The annotation value */ protected final void addDeclaredRepeatable(String annotationName, io.micronaut.core.annotation.AnnotationValue annotationValue) { @@ -765,7 +1152,7 @@ private List values, Map> declaredAnnotations, Map> allAnnotations, + Map> allAnnotations, boolean isDeclared) { if (isDeclared && declaredAnnotations != null) { putValues(annotation, values, declaredAnnotations); @@ -858,6 +1245,40 @@ private Map> getAnnotationsByStereotypeInternal() { return annotations; } + private @Nullable Object getRawSingleValue(@Nonnull String annotation, @Nonnull String member, @Nullable Function valueMapper) { + Object rawValue = getRawValue(annotation, member); + if (rawValue != null) { + if (rawValue.getClass().isArray()) { + int len = Array.getLength(rawValue); + if (len > 0) { + rawValue = Array.get(rawValue, 0); + } + } + } + if (valueMapper != null && rawValue instanceof CharSequence) { + return valueMapper.apply(rawValue); + } else { + return rawValue; + } + } + + @Nullable + private Object getRawValue(@Nonnull String annotation, @Nonnull String member) { + Object rawValue = null; + if (allAnnotations != null && StringUtils.isNotEmpty(annotation)) { + Map values = allAnnotations.get(annotation); + if (values != null) { + rawValue = values.get(member); + } else if (allStereotypes != null) { + values = allStereotypes.get(annotation); + if (values != null) { + rawValue = values.get(member); + } + } + } + return rawValue; + } + private void addRepeatableInternal(String annotationName, io.micronaut.core.annotation.AnnotationValue annotationValue, Map> allAnnotations) { addRepeatableInternal(annotationName, AnnotationMetadata.VALUE_MEMBER, annotationValue, allAnnotations); } @@ -902,10 +1323,10 @@ private void addRepeatableInternal(String annotationName, String member, io.micr */ @Internal public static AnnotationMetadata mutateMember( - AnnotationMetadata annotationMetadata, - String annotationName, - String member, - Object value) { + AnnotationMetadata annotationMetadata, + String annotationName, + String member, + Object value) { return mutateMember(annotationMetadata, annotationName, Collections.singletonMap(member, value)); } @@ -914,6 +1335,7 @@ public static AnnotationMetadata mutateMember( * Contributes defaults to the given target. * *

WARNING: for internal use only be the framework

+ * * @param target The target * @param source The source */ @@ -952,7 +1374,7 @@ public static AnnotationMetadata mutateMember( throw new IllegalArgumentException("Argument [annotationName] cannot be blank"); } if (!members.isEmpty()) { - for (Map.Entry entry: members.entrySet()) { + for (Map.Entry entry : members.entrySet()) { if (StringUtils.isEmpty(entry.getKey())) { throw new IllegalArgumentException("Argument [members] cannot have a blank key"); } diff --git a/inject/src/main/java/io/micronaut/inject/annotation/EnvironmentAnnotationValue.java b/inject/src/main/java/io/micronaut/inject/annotation/EnvironmentAnnotationValue.java index 25a00556886..02f065d8720 100644 --- a/inject/src/main/java/io/micronaut/inject/annotation/EnvironmentAnnotationValue.java +++ b/inject/src/main/java/io/micronaut/inject/annotation/EnvironmentAnnotationValue.java @@ -41,7 +41,15 @@ class EnvironmentAnnotationValue extends AnnotationValue { + if (o instanceof String) { + String v = (String) o; + if (v.contains("${")) { + return environment.getPlaceholderResolver().resolveRequiredPlaceholders(v); + } + } + return o; + } : null); } } diff --git a/inject/src/main/java/io/micronaut/inject/qualifiers/AnnotationMetadataQualifier.java b/inject/src/main/java/io/micronaut/inject/qualifiers/AnnotationMetadataQualifier.java index 4b70568c36a..879095fe985 100644 --- a/inject/src/main/java/io/micronaut/inject/qualifiers/AnnotationMetadataQualifier.java +++ b/inject/src/main/java/io/micronaut/inject/qualifiers/AnnotationMetadataQualifier.java @@ -63,7 +63,7 @@ class AnnotationMetadataQualifier extends NameQualifier { @Override public > Stream reduce(Class beanType, Stream candidates) { String name; - String v = annotationMetadata.getValue(Named.class, String.class).orElse(null); + String v = annotationMetadata.stringValue(Named.class).orElse(null); if (StringUtils.isNotEmpty(v)) { name = Character.toUpperCase(v.charAt(0)) + v.substring(1); return reduceByName(beanType, candidates, name); diff --git a/inject/src/main/java/io/micronaut/inject/qualifiers/NameQualifier.java b/inject/src/main/java/io/micronaut/inject/qualifiers/NameQualifier.java index 43c22056e6b..5d0d9992f53 100644 --- a/inject/src/main/java/io/micronaut/inject/qualifiers/NameQualifier.java +++ b/inject/src/main/java/io/micronaut/inject/qualifiers/NameQualifier.java @@ -119,7 +119,7 @@ protected > Stream reduceByAnnotation(Class beanTy if (candidate instanceof NameResolver) { candidateName = ((NameResolver) candidate).resolveName().orElse(candidate.getBeanType().getSimpleName()); } else { - Optional annotation = candidate.getAnnotationMetadata().getValue(Named.class, String.class); + Optional annotation = candidate.getAnnotationMetadata().stringValue(Named.class); candidateName = annotation.orElse(candidate.getBeanType().getSimpleName()); } @@ -153,7 +153,7 @@ protected > Stream reduceByName(Class beanType, St if (candidate instanceof NameResolver) { candidateName = ((NameResolver) candidate).resolveName().orElse(candidate.getBeanType().getSimpleName()); } else { - Optional annotation = candidate.getAnnotationMetadata().getValue(Named.class, String.class); + Optional annotation = candidate.getAnnotationMetadata().stringValue(Named.class); candidateName = annotation.orElse(candidate.getBeanType().getSimpleName()); } diff --git a/inject/src/main/java/io/micronaut/inject/qualifiers/Qualifiers.java b/inject/src/main/java/io/micronaut/inject/qualifiers/Qualifiers.java index 54634181a84..84510e99b43 100644 --- a/inject/src/main/java/io/micronaut/inject/qualifiers/Qualifiers.java +++ b/inject/src/main/java/io/micronaut/inject/qualifiers/Qualifiers.java @@ -87,7 +87,7 @@ public static Qualifier byAnnotation(AnnotationMetadata metadata, Class value = metadata.getValue(type, String.class); + Optional value = metadata.stringValue(type); if (value.isPresent()) { return byName(value.get()); } diff --git a/management/src/main/java/io/micronaut/management/endpoint/EndpointSensitivityProcessor.java b/management/src/main/java/io/micronaut/management/endpoint/EndpointSensitivityProcessor.java index 73497824968..61d0674c607 100644 --- a/management/src/main/java/io/micronaut/management/endpoint/EndpointSensitivityProcessor.java +++ b/management/src/main/java/io/micronaut/management/endpoint/EndpointSensitivityProcessor.java @@ -60,7 +60,7 @@ public Map getEndpointMethods() { @Override public void process(BeanDefinition beanDefinition, ExecutableMethod method) { - Optional optionalId = beanDefinition.getValue(Endpoint.class, String.class); + Optional optionalId = beanDefinition.stringValue(Endpoint.class); optionalId.ifPresent((id) -> { EndpointConfiguration configuration = endpointConfigurations.stream() @@ -71,8 +71,7 @@ public void process(BeanDefinition beanDefinition, ExecutableMethod met boolean sensitive = configuration .isSensitive() .orElseGet(() -> beanDefinition - .getValue(Endpoint.class, "defaultSensitive", Boolean.class) - .orElse(Endpoint.SENSITIVE)); + .isTrue(Endpoint.class, "defaultSensitive")); endpointMethods.put(method, sensitive); }); diff --git a/management/src/main/java/io/micronaut/management/endpoint/processors/AbstractEndpointRouteBuilder.java b/management/src/main/java/io/micronaut/management/endpoint/processors/AbstractEndpointRouteBuilder.java index fc6107ec15e..ef1a8409be1 100644 --- a/management/src/main/java/io/micronaut/management/endpoint/processors/AbstractEndpointRouteBuilder.java +++ b/management/src/main/java/io/micronaut/management/endpoint/processors/AbstractEndpointRouteBuilder.java @@ -118,7 +118,7 @@ protected Optional resolveActiveEndPointId(Class declaringType) { if (opt.isPresent()) { BeanDefinition beanDefinition = opt.get(); if (beanDefinition.hasStereotype(Endpoint.class)) { - String id = beanDefinition.getValue(Endpoint.class, String.class).orElse(null); + String id = beanDefinition.stringValue(Endpoint.class).orElse(null); if (id == null || !ENDPOINT_ID_PATTERN.matcher(id).matches()) { id = NameUtils.hyphenate(beanDefinition.getName()); } diff --git a/router/src/main/java/io/micronaut/web/router/AbstractRouteMatch.java b/router/src/main/java/io/micronaut/web/router/AbstractRouteMatch.java index fc01ab9fa00..6b2962ddf54 100644 --- a/router/src/main/java/io/micronaut/web/router/AbstractRouteMatch.java +++ b/router/src/main/java/io/micronaut/web/router/AbstractRouteMatch.java @@ -366,7 +366,7 @@ protected Object resolveValueOrError(Argument argument, ConversionContext conver protected abstract RouteMatch newFulfilled(Map newVariables, List requiredArguments); private String resolveInputName(Argument requiredArgument) { - String inputName = requiredArgument.getAnnotationMetadata().getValue(Bindable.class, String.class).orElse(null); + String inputName = requiredArgument.getAnnotationMetadata().stringValue(Bindable.class).orElse(null); if (StringUtils.isEmpty(inputName)) { inputName = requiredArgument.getName(); } diff --git a/router/src/main/java/io/micronaut/web/router/AnnotatedFilterRouteBuilder.java b/router/src/main/java/io/micronaut/web/router/AnnotatedFilterRouteBuilder.java index a6e57974151..45219df900d 100644 --- a/router/src/main/java/io/micronaut/web/router/AnnotatedFilterRouteBuilder.java +++ b/router/src/main/java/io/micronaut/web/router/AnnotatedFilterRouteBuilder.java @@ -69,7 +69,7 @@ public void process() { // ignore http client filters continue; } - String[] patterns = beanDefinition.getValue(Filter.class, String[].class).orElse(null); + String[] patterns = beanDefinition.stringValues(Filter.class); if (ArrayUtils.isNotEmpty(patterns)) { HttpMethod[] methods = beanDefinition.getValue(Filter.class, "methods", HttpMethod[].class).orElse(null); String first = patterns[0]; diff --git a/router/src/main/java/io/micronaut/web/router/AnnotatedMethodRouteBuilder.java b/router/src/main/java/io/micronaut/web/router/AnnotatedMethodRouteBuilder.java index 8bfe8dedd9d..88e90162c22 100644 --- a/router/src/main/java/io/micronaut/web/router/AnnotatedMethodRouteBuilder.java +++ b/router/src/main/java/io/micronaut/web/router/AnnotatedMethodRouteBuilder.java @@ -17,21 +17,25 @@ import io.micronaut.context.ExecutionHandleLocator; import io.micronaut.context.processor.ExecutableMethodProcessor; -import io.micronaut.core.annotation.AnnotationValue; +import io.micronaut.core.annotation.AnnotationMetadata; import io.micronaut.core.convert.ConversionService; import io.micronaut.core.reflect.ClassLoadingReporter; +import io.micronaut.core.util.ArrayUtils; import io.micronaut.core.util.StringUtils; import io.micronaut.http.HttpStatus; import io.micronaut.http.MediaType; -import io.micronaut.http.annotation.*; import io.micronaut.http.annotation.Error; +import io.micronaut.http.annotation.*; import io.micronaut.http.uri.UriTemplate; import io.micronaut.inject.BeanDefinition; import io.micronaut.inject.ExecutableMethod; import javax.inject.Singleton; import java.lang.annotation.Annotation; -import java.util.*; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; import java.util.function.BiConsumer; /** @@ -44,6 +48,7 @@ @Singleton public class AnnotatedMethodRouteBuilder extends DefaultRouteBuilder implements ExecutableMethodProcessor { + private static final MediaType[] DEFAULT_MEDIA_TYPES = {MediaType.APPLICATION_JSON_TYPE}; private final Map> httpMethodsHandlers = new LinkedHashMap<>(); /** @@ -54,9 +59,8 @@ public class AnnotatedMethodRouteBuilder extends DefaultRouteBuilder implements public AnnotatedMethodRouteBuilder(ExecutionHandleLocator executionHandleLocator, UriNamingStrategy uriNamingStrategy, ConversionService conversionService) { super(executionHandleLocator, uriNamingStrategy, conversionService); httpMethodsHandlers.put(Get.class, (BeanDefinition bean, ExecutableMethod method) -> { - AnnotationValue mapping = method.getAnnotation(HttpMethodMapping.class); - String uri = mapping.getRequiredValue(String.class); - MediaType[] produces = method.getValue(Produces.class, MediaType[].class).orElse(null); + String uri = method.stringValue(HttpMethodMapping.class).orElse(UriMapping.DEFAULT_URI); + MediaType[] produces = resolveProduces(method); Route route = GET(resolveUri(bean, uri, method, uriNamingStrategy), @@ -68,10 +72,9 @@ public AnnotatedMethodRouteBuilder(ExecutionHandleLocator executionHandleLocator }); httpMethodsHandlers.put(Post.class, (BeanDefinition bean, ExecutableMethod method) -> { - AnnotationValue mapping = method.getAnnotation(HttpMethodMapping.class); - String uri = mapping.getRequiredValue(String.class); - MediaType[] consumes = method.getValue(Consumes.class, MediaType[].class).orElse(null); - MediaType[] produces = method.getValue(Produces.class, MediaType[].class).orElse(null); + String uri = method.stringValue(HttpMethodMapping.class).orElse(UriMapping.DEFAULT_URI); + MediaType[] consumes = resolveConsumes(method); + MediaType[] produces = resolveProduces(method); Route route = POST(resolveUri(bean, uri, method, uriNamingStrategy), @@ -84,10 +87,9 @@ public AnnotatedMethodRouteBuilder(ExecutionHandleLocator executionHandleLocator }); httpMethodsHandlers.put(Put.class, (BeanDefinition bean, ExecutableMethod method) -> { - AnnotationValue mapping = method.getAnnotation(HttpMethodMapping.class); - String uri = mapping.getRequiredValue(String.class); - MediaType[] consumes = method.getValue(Consumes.class, MediaType[].class).orElse(null); - MediaType[] produces = method.getValue(Produces.class, MediaType[].class).orElse(null); + String uri = method.stringValue(HttpMethodMapping.class).orElse(UriMapping.DEFAULT_URI); + MediaType[] consumes = resolveConsumes(method); + MediaType[] produces = resolveProduces(method); Route route = PUT(resolveUri(bean, uri, method, uriNamingStrategy), @@ -100,10 +102,9 @@ public AnnotatedMethodRouteBuilder(ExecutionHandleLocator executionHandleLocator }); httpMethodsHandlers.put(Patch.class, (BeanDefinition bean, ExecutableMethod method) -> { - AnnotationValue mapping = method.getAnnotation(HttpMethodMapping.class); - String uri = mapping.getRequiredValue(String.class); - MediaType[] consumes = method.getValue(Consumes.class, MediaType[].class).orElse(null); - MediaType[] produces = method.getValue(Produces.class, MediaType[].class).orElse(null); + String uri = method.stringValue(HttpMethodMapping.class).orElse(UriMapping.DEFAULT_URI); + MediaType[] consumes = resolveConsumes(method); + MediaType[] produces = resolveProduces(method); Route route = PATCH(resolveUri(bean, uri, method, uriNamingStrategy), @@ -116,10 +117,9 @@ public AnnotatedMethodRouteBuilder(ExecutionHandleLocator executionHandleLocator }); httpMethodsHandlers.put(Delete.class, (BeanDefinition bean, ExecutableMethod method) -> { - AnnotationValue mapping = method.getAnnotation(HttpMethodMapping.class); - String uri = mapping.getRequiredValue(String.class); - MediaType[] consumes = method.getValue(Consumes.class, MediaType[].class).orElse(null); - MediaType[] produces = method.getValue(Produces.class, MediaType[].class).orElse(null); + String uri = method.stringValue(HttpMethodMapping.class).orElse(UriMapping.DEFAULT_URI); + MediaType[] consumes = resolveConsumes(method); + MediaType[] produces = resolveProduces(method); Route route = DELETE(resolveUri(bean, uri, method, uriNamingStrategy), @@ -133,8 +133,7 @@ public AnnotatedMethodRouteBuilder(ExecutionHandleLocator executionHandleLocator httpMethodsHandlers.put(Head.class, (BeanDefinition bean, ExecutableMethod method) -> { - AnnotationValue mapping = method.getAnnotation(HttpMethodMapping.class); - String uri = mapping.getRequiredValue(String.class); + String uri = method.stringValue(HttpMethodMapping.class).orElse(UriMapping.DEFAULT_URI); Route route = HEAD(resolveUri(bean, uri, method, uriNamingStrategy), @@ -146,10 +145,9 @@ public AnnotatedMethodRouteBuilder(ExecutionHandleLocator executionHandleLocator }); httpMethodsHandlers.put(Options.class, (BeanDefinition bean, ExecutableMethod method) -> { - AnnotationValue mapping = method.getAnnotation(HttpMethodMapping.class); - String uri = mapping.getRequiredValue(String.class); - MediaType[] consumes = method.getValue(Consumes.class, MediaType[].class).orElse(null); - MediaType[] produces = method.getValue(Produces.class, MediaType[].class).orElse(null); + String uri = method.stringValue(HttpMethodMapping.class).orElse(UriMapping.DEFAULT_URI); + MediaType[] consumes = resolveConsumes(method); + MediaType[] produces = resolveProduces(method); Route route = OPTIONS(resolveUri(bean, uri, method, uriNamingStrategy), @@ -162,8 +160,7 @@ public AnnotatedMethodRouteBuilder(ExecutionHandleLocator executionHandleLocator }); httpMethodsHandlers.put(Trace.class, (BeanDefinition bean, ExecutableMethod method) -> { - AnnotationValue mapping = method.getAnnotation(HttpMethodMapping.class); - String uri = mapping.getRequiredValue(String.class); + String uri = method.stringValue(HttpMethodMapping.class).orElse(UriMapping.DEFAULT_URI); Route route = TRACE(resolveUri(bean, uri, method, uriNamingStrategy), @@ -175,7 +172,7 @@ public AnnotatedMethodRouteBuilder(ExecutionHandleLocator executionHandleLocator }); httpMethodsHandlers.put(Error.class, (BeanDefinition bean, ExecutableMethod method) -> { - boolean isGlobal = method.getValue(Error.class, "global", boolean.class).orElse(false); + boolean isGlobal = method.isTrue(Error.class, "global"); Class declaringType = bean.getBeanType(); if (method.isPresent(Error.class, "status")) { Optional value = method.getValue(Error.class, "status", HttpStatus.class); @@ -188,7 +185,7 @@ public AnnotatedMethodRouteBuilder(ExecutionHandleLocator executionHandleLocator }); } else { Class exceptionType = null; - if (method.isPresent(Error.class, "value")) { + if (method.isPresent(Error.class, AnnotationMetadata.VALUE_MEMBER)) { Optional annotationValue = method.classValue(Error.class); if (annotationValue.isPresent()) { if (Throwable.class.isAssignableFrom(annotationValue.get())) { @@ -215,6 +212,22 @@ public AnnotatedMethodRouteBuilder(ExecutionHandleLocator executionHandleLocator ); } + private MediaType[] resolveConsumes(ExecutableMethod method) { + MediaType[] consumes = MediaType.of(method.stringValues(Consumes.class)); + if (ArrayUtils.isEmpty(consumes)) { + consumes = DEFAULT_MEDIA_TYPES; + } + return consumes; + } + + private MediaType[] resolveProduces(ExecutableMethod method) { + MediaType[] produces = MediaType.of(method.stringValues(Produces.class)); + if (ArrayUtils.isEmpty(produces)) { + produces = DEFAULT_MEDIA_TYPES; + } + return produces; + } + @Override public void process(BeanDefinition beanDefinition, ExecutableMethod method) { Optional> actionAnn = method.getAnnotationTypeByStereotype(HttpMethodMapping.class); @@ -231,8 +244,8 @@ public void process(BeanDefinition beanDefinition, ExecutableMethod met ); if (!actionAnn.isPresent() && method.isDeclaredAnnotationPresent(UriMapping.class)) { - String uri = method.getValue(UriMapping.class, String.class).orElse(UriMapping.DEFAULT_URI); - MediaType[] produces = method.getValue(Produces.class, MediaType[].class).orElse(null); + String uri = method.stringValue(UriMapping.class).orElse(UriMapping.DEFAULT_URI); + MediaType[] produces = MediaType.of(method.stringValues(Produces.class)); Route route = GET(resolveUri(beanDefinition, uri, method, uriNamingStrategy), diff --git a/router/src/main/java/io/micronaut/web/router/DefaultRouteBuilder.java b/router/src/main/java/io/micronaut/web/router/DefaultRouteBuilder.java index 6ec41018cb5..5998ad697df 100644 --- a/router/src/main/java/io/micronaut/web/router/DefaultRouteBuilder.java +++ b/router/src/main/java/io/micronaut/web/router/DefaultRouteBuilder.java @@ -23,6 +23,7 @@ import io.micronaut.core.naming.NameUtils; import io.micronaut.core.naming.conventions.TypeConvention; import io.micronaut.core.type.Argument; +import io.micronaut.core.util.ArrayUtils; import io.micronaut.core.util.StringUtils; import io.micronaut.http.HttpMethod; import io.micronaut.http.HttpRequest; @@ -428,11 +429,11 @@ abstract class AbstractRoute implements MethodBasedRoute { this.targetMethod = targetMethod; this.conversionService = conversionService; this.acceptedMediaTypes = mediaTypes; - targetMethod.getValue(Produces.class, MediaType[].class).ifPresent(produces -> - this.producesMediaTypes = Arrays.asList(produces) - ); - + MediaType[] types = MediaType.of(targetMethod.stringValues(Produces.class)); + if (ArrayUtils.isNotEmpty(types)) { + this.producesMediaTypes = Arrays.asList(types); + } this.conditions.add(req -> { if (!permitsRequestBody()) { return true; diff --git a/router/src/main/java/io/micronaut/web/router/RouteBuilder.java b/router/src/main/java/io/micronaut/web/router/RouteBuilder.java index 00649661c81..bcd5acc3f0b 100644 --- a/router/src/main/java/io/micronaut/web/router/RouteBuilder.java +++ b/router/src/main/java/io/micronaut/web/router/RouteBuilder.java @@ -1168,8 +1168,8 @@ default String resolveUri(Class type) { */ default @Nonnull String resolveUri(BeanDefinition beanDefinition) { - String uri = beanDefinition.getValue(UriMapping.class, String.class).orElseGet(() -> - beanDefinition.getValue(Controller.class, String.class).orElse(UriMapping.DEFAULT_URI) + String uri = beanDefinition.stringValue(UriMapping.class).orElseGet(() -> + beanDefinition.stringValue(Controller.class).orElse(UriMapping.DEFAULT_URI) ); uri = normalizeUri(uri); if (uri != null) { diff --git a/router/src/main/java/io/micronaut/web/router/naming/HyphenatedUriNamingStrategy.java b/router/src/main/java/io/micronaut/web/router/naming/HyphenatedUriNamingStrategy.java index ab783037891..a616cc05167 100644 --- a/router/src/main/java/io/micronaut/web/router/naming/HyphenatedUriNamingStrategy.java +++ b/router/src/main/java/io/micronaut/web/router/naming/HyphenatedUriNamingStrategy.java @@ -43,8 +43,8 @@ public String resolveUri(Class type) { @Override public @Nonnull String resolveUri(BeanDefinition beanDefinition) { - String uri = beanDefinition.getValue(UriMapping.class, String.class).orElseGet(() -> - beanDefinition.getValue(Controller.class, String.class).orElse(UriMapping.DEFAULT_URI) + String uri = beanDefinition.stringValue(UriMapping.class).orElseGet(() -> + beanDefinition.stringValue(Controller.class).orElse(UriMapping.DEFAULT_URI) ); return normalizeUri(uri); } diff --git a/router/src/main/java/io/micronaut/web/router/qualifier/ConsumesMediaTypeQualifier.java b/router/src/main/java/io/micronaut/web/router/qualifier/ConsumesMediaTypeQualifier.java index 142dfdfd5af..1e5336de764 100644 --- a/router/src/main/java/io/micronaut/web/router/qualifier/ConsumesMediaTypeQualifier.java +++ b/router/src/main/java/io/micronaut/web/router/qualifier/ConsumesMediaTypeQualifier.java @@ -16,13 +16,13 @@ package io.micronaut.web.router.qualifier; import io.micronaut.context.Qualifier; +import io.micronaut.core.util.ArrayUtils; import io.micronaut.http.HttpHeaders; import io.micronaut.http.MediaType; import io.micronaut.http.annotation.Consumes; import io.micronaut.inject.BeanType; import java.util.Arrays; -import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -49,14 +49,13 @@ public ConsumesMediaTypeQualifier(MediaType contentType) { @Override public > Stream reduce(Class beanType, Stream candidates) { return candidates.filter(candidate -> { - Optional consumes = candidate.getAnnotationMetadata().getValue(Consumes.class, MediaType[].class); - if (consumes.isPresent()) { - Set consumedTypes = Arrays.stream(consumes.get()).map(MediaType::getExtension).collect(Collectors.toSet()); - return consumedTypes.contains(contentType.getExtension()); - } - return false; + MediaType[] consumes = MediaType.of(candidate.getAnnotationMetadata().stringValues(Consumes.class)); + if (ArrayUtils.isNotEmpty(consumes)) { + Set consumedTypes = Arrays.stream(consumes).map(MediaType::getExtension).collect(Collectors.toSet()); + return consumedTypes.contains(contentType.getExtension()); } - ); + return false; + }); } @Override diff --git a/router/src/main/java/io/micronaut/web/router/qualifier/ProducesMediaTypeQualifier.java b/router/src/main/java/io/micronaut/web/router/qualifier/ProducesMediaTypeQualifier.java index 920386fdd61..a6412038343 100644 --- a/router/src/main/java/io/micronaut/web/router/qualifier/ProducesMediaTypeQualifier.java +++ b/router/src/main/java/io/micronaut/web/router/qualifier/ProducesMediaTypeQualifier.java @@ -16,13 +16,13 @@ package io.micronaut.web.router.qualifier; import io.micronaut.context.Qualifier; +import io.micronaut.core.util.ArrayUtils; import io.micronaut.http.HttpHeaders; import io.micronaut.http.MediaType; import io.micronaut.http.annotation.Produces; import io.micronaut.inject.BeanType; import java.util.Arrays; -import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -49,14 +49,13 @@ public ProducesMediaTypeQualifier(MediaType contentType) { @Override public > Stream reduce(Class beanType, Stream candidates) { return candidates.filter(candidate -> { - Optional consumes = candidate.getAnnotationMetadata().getValue(Produces.class, MediaType[].class); - if (consumes.isPresent()) { - Set consumedTypes = Arrays.stream(consumes.get()).map(MediaType::getExtension).collect(Collectors.toSet()); - return consumedTypes.contains(contentType.getExtension()); - } - return false; + MediaType[] consumes = MediaType.of(candidate.getAnnotationMetadata().stringValues(Produces.class)); + if (ArrayUtils.isNotEmpty(consumes)) { + Set consumedTypes = Arrays.stream(consumes).map(MediaType::getExtension).collect(Collectors.toSet()); + return consumedTypes.contains(contentType.getExtension()); } - ); + return false; + }); } @Override diff --git a/runtime/src/main/java/io/micronaut/jackson/modules/BeanIntrospectionModule.java b/runtime/src/main/java/io/micronaut/jackson/modules/BeanIntrospectionModule.java index 96c19642238..3064627b13b 100644 --- a/runtime/src/main/java/io/micronaut/jackson/modules/BeanIntrospectionModule.java +++ b/runtime/src/main/java/io/micronaut/jackson/modules/BeanIntrospectionModule.java @@ -84,11 +84,12 @@ private PropertyMetadata newPropertyMetadata(Argument argument, AnnotationMet final Boolean required = argument.isAnnotationPresent(Nonnull.class) || annotationMetadata.getValue(JsonProperty.class, "required", Boolean.class).orElse(false); + int index = annotationMetadata.intValue(JsonProperty.class, "index").orElse(-1); return PropertyMetadata.construct( required, - annotationMetadata.getValue(JsonPropertyDescription.class, String.class).orElse(null), - annotationMetadata.getValue(JsonProperty.class, "index", Integer.class).orElse(null), - annotationMetadata.getValue(JsonProperty.class, "defaultValue", String.class).orElse(null) + annotationMetadata.stringValue(JsonPropertyDescription.class).orElse(null), + index > -1 ? index : null, + annotationMetadata.stringValue(JsonProperty.class, "defaultValue").orElse(null) ); } @@ -136,10 +137,10 @@ public JsonSerializer build() { } else if ("links".equals(n)) { propertyName = Resource.LINKS; } else { - propertyName = beanProperty.getValue(JsonProperty.class, String.class).orElse(beanProperty.getName()); + propertyName = beanProperty.stringValue(JsonProperty.class).orElse(beanProperty.getName()); } } else { - propertyName = beanProperty.getValue(JsonProperty.class, String.class).orElse(beanProperty.getName()); + propertyName = beanProperty.stringValue(JsonProperty.class).orElse(beanProperty.getName()); } BeanPropertyWriter writer = new BeanIntrospectionPropertyWriter( propertyName, @@ -159,7 +160,7 @@ public JsonSerializer build() { final List newProperties = new ArrayList<>(properties); Map named = new LinkedHashMap<>(properties.size()); for (BeanProperty beanProperty : beanProperties) { - final Optional n = beanProperty.getValue(JsonProperty.class, String.class); + final Optional n = beanProperty.stringValue(JsonProperty.class); n.ifPresent(s -> named.put(s, beanProperty)); } for (int i = 0; i < properties.size(); i++) { @@ -275,7 +276,7 @@ public SettableBeanProperty[] getFromObjectArguments(DeserializationConfig confi final JavaType javaType = newType(argument, typeFactory); final AnnotationMetadata annotationMetadata = argument.getAnnotationMetadata(); PropertyMetadata propertyMetadata = newPropertyMetadata(argument, annotationMetadata); - final String simpleName = annotationMetadata.getValue(JsonProperty.class, String.class).orElse(argument.getName()); + final String simpleName = annotationMetadata.stringValue(JsonProperty.class).orElse(argument.getName()); props[i] = new CreatorProperty( PropertyName.construct(simpleName), javaType, diff --git a/runtime/src/main/java/io/micronaut/runtime/context/scope/refresh/RefreshScope.java b/runtime/src/main/java/io/micronaut/runtime/context/scope/refresh/RefreshScope.java index 11f235d2015..ef9f201364b 100644 --- a/runtime/src/main/java/io/micronaut/runtime/context/scope/refresh/RefreshScope.java +++ b/runtime/src/main/java/io/micronaut/runtime/context/scope/refresh/RefreshScope.java @@ -37,9 +37,7 @@ import javax.inject.Named; import javax.inject.Provider; import javax.inject.Singleton; -import java.util.Arrays; import java.util.Collection; -import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -189,20 +187,14 @@ private void disposeOfBeanSubset(Collection keys) { for (String beanKey : refreshableBeans.keySet()) { BeanRegistration beanRegistration = refreshableBeans.get(beanKey); BeanDefinition definition = beanRegistration.getBeanDefinition(); - Optional opt = definition.getValue(Refreshable.class, String[].class); - if (opt.isPresent()) { - String[] strings = opt.get(); - if (!ArrayUtils.isEmpty(strings)) { - List prefixes = Arrays.asList(strings); - for (String prefix : prefixes) { - for (String k : keys) { - if (k.startsWith(prefix)) { - disposeOfBean(beanKey); - } + String[] strings = definition.stringValues(Refreshable.class); + if (!ArrayUtils.isEmpty(strings)) { + for (String prefix : strings) { + for (String k : keys) { + if (k.startsWith(prefix)) { + disposeOfBean(beanKey); } } - } else { - disposeOfBean(beanKey); } } else { disposeOfBean(beanKey); diff --git a/runtime/src/main/java/io/micronaut/runtime/converters/time/TimeConverterRegistrar.java b/runtime/src/main/java/io/micronaut/runtime/converters/time/TimeConverterRegistrar.java index 3136c986de0..91bc2b6b2b9 100644 --- a/runtime/src/main/java/io/micronaut/runtime/converters/time/TimeConverterRegistrar.java +++ b/runtime/src/main/java/io/micronaut/runtime/converters/time/TimeConverterRegistrar.java @@ -208,7 +208,7 @@ public void register(ConversionService conversionService) { } private DateTimeFormatter resolveFormatter(ConversionContext context) { - Optional format = context.getAnnotationMetadata().getValue(Format.class, String.class); + Optional format = context.getAnnotationMetadata().stringValue(Format.class); return format .map((pattern) -> DateTimeFormatter.ofPattern(pattern, context.getLocale())) .orElse(DateTimeFormatter.RFC_1123_DATE_TIME); diff --git a/runtime/src/main/java/io/micronaut/scheduling/async/AsyncInterceptor.java b/runtime/src/main/java/io/micronaut/scheduling/async/AsyncInterceptor.java index 19797422e23..1a49fccfc3f 100644 --- a/runtime/src/main/java/io/micronaut/scheduling/async/AsyncInterceptor.java +++ b/runtime/src/main/java/io/micronaut/scheduling/async/AsyncInterceptor.java @@ -57,7 +57,7 @@ public class AsyncInterceptor implements MethodInterceptor { @Override public Object intercept(MethodInvocationContext context) { - String executorName = context.getValue(Async.class, String.class).orElse(TaskExecutors.SCHEDULED); + String executorName = context.stringValue(Async.class).orElse(TaskExecutors.SCHEDULED); ExecutorService executorService = beanLocator.findBean(ExecutorService.class, Qualifiers.byName(executorName)).orElseThrow(() -> new TaskExecutionException("No ExecutorService named [" + executorName + "] configured in application context") ); diff --git a/settings.gradle b/settings.gradle index 4135962c064..1160d394a0a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -39,6 +39,9 @@ include "test-suite-kotlin" include "test-suite-groovy" include "test-utils" +// benchmarks +include "benchmarks" + // configurations include "configurations:cassandra" include "configurations:hibernate-validator" diff --git a/views/src/main/java/io/micronaut/views/ViewsFilter.java b/views/src/main/java/io/micronaut/views/ViewsFilter.java index 596404b371f..9a7c6c51daa 100644 --- a/views/src/main/java/io/micronaut/views/ViewsFilter.java +++ b/views/src/main/java/io/micronaut/views/ViewsFilter.java @@ -114,8 +114,8 @@ public final Publisher> doFilter(HttpRequest request, if (optionalView.isPresent()) { - MediaType type = route.getValue(Produces.class, MediaType.class) - .orElse((route.getValue(View.class).isPresent() || body instanceof ModelAndView) ? MediaType.TEXT_HTML_TYPE : MediaType.APPLICATION_JSON_TYPE); + MediaType type = route.stringValue(Produces.class).map(MediaType::new) + .orElse((route.isPresent(View.class, AnnotationMetadata.VALUE_MEMBER) || body instanceof ModelAndView) ? MediaType.TEXT_HTML_TYPE : MediaType.APPLICATION_JSON_TYPE); Optional optionalViewsRenderer = beanLocator.findBean(ViewsRenderer.class, new ProducesMediaTypeQualifier<>(type)); @@ -203,9 +203,9 @@ protected Object resolveModel(Object responseBody) { */ @SuppressWarnings("WeakerAccess") protected Optional resolveView(AnnotationMetadata route, Object responseBody) { - Optional optionalViewName = route.getValue(View.class); + Optional optionalViewName = route.stringValue(View.class); if (optionalViewName.isPresent()) { - return Optional.of((String) optionalViewName.get()); + return optionalViewName; } else if (responseBody instanceof ModelAndView) { return ((ModelAndView) responseBody).getView(); } diff --git a/views/src/main/java/io/micronaut/views/csp/CspFilter.java b/views/src/main/java/io/micronaut/views/csp/CspFilter.java index 756789fbf1e..ff01ebcf6f2 100644 --- a/views/src/main/java/io/micronaut/views/csp/CspFilter.java +++ b/views/src/main/java/io/micronaut/views/csp/CspFilter.java @@ -77,14 +77,12 @@ public CspFilter(CspConfiguration cspConfiguration) { public Publisher> doFilter(HttpRequest request, ServerFilterChain chain) { return Flowable.fromPublisher(chain.proceed(request)) - .doOnNext(response -> { - cspConfiguration.getPolicyDirectives() - .map(StringUtils::trimToNull) - .ifPresent(directives -> { - String header = cspConfiguration.isReportOnly() ? CSP_REPORT_ONLY_HEADER : CSP_HEADER; - response.getHeaders().add(header, directives); - }); - }); + .doOnNext(response -> cspConfiguration.getPolicyDirectives() + .map(StringUtils::trimToNull) + .ifPresent(directives -> { + String header = cspConfiguration.isReportOnly() ? CSP_REPORT_ONLY_HEADER : CSP_HEADER; + response.getHeaders().add(header, directives); + })); } }