Skip to content

Commit

Permalink
Performance optimizations
Browse files Browse the repository at this point in the history
  • Loading branch information
dstepanov committed Aug 21, 2023
1 parent eec3578 commit b9b93dd
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public final class AnnotationClassValue<T> implements CharSequence, Named {
public static final AnnotationClassValue<?>[] EMPTY_ARRAY = new AnnotationClassValue[0];

private final String name;
private final Class<T> theClass;
final Class<T> theClass;
private final T instance;
private final boolean instantiated;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,21 @@
import java.lang.annotation.Annotation;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Array;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.OptionalLong;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* A runtime representation of the annotation and its values.
Expand Down Expand Up @@ -1564,8 +1575,11 @@ public static Class<?>[] resolveClassValues(@Nullable Object value) {
if (len == 1) {
return annotationValues[0].classValues();
} else {
return Arrays.stream(annotationValues)
.flatMap(annotationValue -> Stream.of(annotationValue.classValues())).toArray(Class[]::new);
List<Class<?>> list = new ArrayList<>(5);
for (AnnotationValue<?> annotationValue : annotationValues) {
list.addAll(Arrays.asList(annotationValue.classValues()));
}
return list.toArray(new Class[0]);
}
}
return null;
Expand All @@ -1577,14 +1591,18 @@ public static Class<?>[] resolveClassValues(@Nullable Object value) {
if (values instanceof Class<?>[] classes) {
return classes;
} else {
return Arrays.stream(values).flatMap(o -> {
List<Class<?>> list = new ArrayList<>(5);
for (Object o : values) {
if (o instanceof AnnotationClassValue<?> annotationClassValue) {
return annotationClassValue.getType().stream();
if (annotationClassValue.theClass != null) {
list.add(annotationClassValue.theClass);
}
} else if (o instanceof Class<?> aClass) {
return Stream.of(aClass);
list.add(aClass);
}
return Stream.empty();
}).toArray(Class[]::new);
}
return list.toArray(new Class[0]);

}
}
if (value instanceof Class<?> aClass) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public class RouteValidationVisitor implements TypeElementVisitor<Object, Object
static final String MICRONAUT_PROCESSING_INCREMENTAL = "micronaut.processing.incremental";
static final String VALIDATION_OPTION = "micronaut.route.validation";
private static final String METHOD_MAPPING_ANN = "io.micronaut.http.annotation.HttpMethodMapping";
private List<RouteValidationRule> rules = new ArrayList<>();
private final List<RouteValidationRule> rules = new ArrayList<>();
private boolean skipValidation = false;
private final DefaultPropertyPlaceholderResolver resolver = new DefaultPropertyPlaceholderResolver(null, ConversionService.SHARED);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,17 @@ public String getPrefix() {

@Override
public Optional<String> resolvePlaceholders(String str) {
try {
return Optional.of(resolveRequiredPlaceholders(str));
} catch (ConfigurationException e) {
return Optional.empty();
List<Segment> segments = buildSegments(str, false);
StringBuilder value = new StringBuilder();
for (Segment segment: segments) {
Optional<String> resolved = segment.findValue(String.class);
if (resolved.isPresent()) {
value.append(resolved.get());
} else {
return Optional.empty();
}
}
return Optional.of(value.toString());
}

@Override
Expand Down Expand Up @@ -131,13 +137,35 @@ public <T> T resolveRequiredPlaceholder(String str, Class<T> type) throws Config
}
}

@Override
public <T> Optional<T> resolveOptionalPlaceholder(String str, Class<T> type) throws ConfigurationException {
List<Segment> segments = buildSegments(str, false);
if (segments.size() == 1) {
return segments.get(0).findValue(type);
} else {
return Optional.empty();
}
}

/**
* Split a placeholder value into logic segments.
*
* @param str The placeholder
* @return The list of segments
*/
public List<Segment> buildSegments(String str) {
return buildSegments(str, true);
}

/**
* Split a placeholder value into logic segments.
*
* @param str The placeholder
* @param failOnIncomplete True if should fail on incomplete placeholder, empty list will be returned otherwise
* @return The list of segments
* @since 4.2.0
*/
private List<Segment> buildSegments(String str, boolean failOnIncomplete) {
List<Segment> segments = new ArrayList<>();
String value = str;
int i = value.indexOf(PREFIX);
Expand All @@ -153,15 +181,15 @@ public List<Segment> buildSegments(String str) {
if (suffixIdx > -1) {
String expr = value.substring(0, suffixIdx).trim();
segments.add(new PlaceholderSegment(expr));
if (value.length() > suffixIdx) {
value = value.substring(suffixIdx + SUFFIX.length());
}
} else {
value = value.substring(suffixIdx + SUFFIX.length());
} else if (failOnIncomplete) {
throw new ConfigurationException("Incomplete placeholder definitions detected: " + str);
} else {
return List.of();
}
i = value.indexOf(PREFIX);
}
if (value.length() > 0) {
if (!value.isEmpty()) {
segments.add(new RawSegment(value));
}
return segments;
Expand Down Expand Up @@ -200,6 +228,34 @@ protected <T> T resolveExpression(String context, String expression, Class<T> ty
return null;
}

/**
* Resolves a single optional expression.
*
* @param expression The expression
* @param type The class
* @param <T> The type the expression should be converted to
* @return The resolved and converted expression
*/
private <T> Optional<T> resolveOptionalExpression(String expression, Class<T> type) {
for (PropertyExpressionResolver expressionResolver : getExpressionResolvers()) {
Optional<T> value = expressionResolver.resolve(environment, conversionService, expression, type);
if (value.isPresent()) {
return value;
}
}
Optional<T> property = environment.getProperty(expression, type);
if (property.isPresent()) {
return property;
}
if (NameUtils.isEnvironmentName(expression)) {
String envVar = CachedEnvironment.getenv(expression);
if (StringUtils.isNotEmpty(envVar)) {
return conversionService.convert(envVar, type);
}
}
return Optional.empty();
}

@Override
public void close() throws Exception {
if (expressionResolvers != null) {
Expand Down Expand Up @@ -229,6 +285,23 @@ public interface Segment {
* @throws ConfigurationException If any error occurs
*/
<T> T getValue(Class<T> type) throws ConfigurationException;

/**
* Returns the optional value of a given segment converted to
* the provided type. Any conversions errors are ignored.
*
* @param type The class
* @param <T> The type to convert the value to
* @return The converted optional value
* @since 4.2.0
*/
default <T> Optional<T> findValue(Class<T> type) {
try {
return Optional.of(getValue(type));
} catch (ConfigurationException e) {
return Optional.empty();
}
}
}

/**
Expand Down Expand Up @@ -260,6 +333,15 @@ public <T> T getValue(Class<T> type) throws ConfigurationException {
new ConfigurationException("Could not convert: [" + text + "] to the required type: [" + type.getName() + "]"));
}
}

@Override
public <T> Optional<T> findValue(Class<T> type) {
if (type.isInstance(text)) {
return Optional.of((T) text);
} else {
return Optional.empty();
}
}
}

/**
Expand Down Expand Up @@ -309,7 +391,20 @@ public <T> T getValue(Class<T> type) throws ConfigurationException {
} else {
throw new ConfigurationException("Could not resolve placeholder ${" + placeholder + "}");
}
}

@Override
public <T> Optional<T> findValue(Class<T> type) {
for (String expression: expressions) {
Optional<T> optionalValue = resolveOptionalExpression(expression, type);
if (optionalValue.isPresent()) {
return optionalValue;
}
}
if (defaultValue != null) {
return conversionService.convert(defaultValue, type);
}
return Optional.empty();
}

private void findExpressions(String placeholder) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,21 @@ public interface PropertyPlaceholderResolver {
default @NonNull <T> T resolveRequiredPlaceholder(String str, Class<T> type) throws ConfigurationException {
throw new ConfigurationException("Unsupported operation");
}

/**
* Resolves the optional value of a single placeholder.
*
* @param str The string containing the placeholder
* @param type The class of the type
* @param <T> The type the value should be converted to
* @return The resolved optional value
* @since 4.2.0
*/
default <T> Optional<T> resolveOptionalPlaceholder(String str, Class<T> type) throws ConfigurationException {
try {
return Optional.of(resolveRequiredPlaceholder(str, type));
} catch (ConfigurationException e) {
return Optional.empty();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

import io.micronaut.context.env.Environment;
import io.micronaut.context.env.PropertyPlaceholderResolver;
import io.micronaut.context.exceptions.ConfigurationException;
import io.micronaut.core.annotation.AnnotationClassValue;
import io.micronaut.core.annotation.AnnotationValue;
import io.micronaut.core.annotation.Internal;
Expand All @@ -31,18 +30,17 @@
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* Extended version of {@link ConvertibleValuesMap} that resolves placeholders based on the environment.
*
* @param <V> generic valu
* @param <V> generic value
* @author graemerocher
* @since 1.0
*/
@Internal
class EnvironmentConvertibleValuesMap<V> extends ConvertibleValuesMap<V> {
final class EnvironmentConvertibleValuesMap<V> extends ConvertibleValuesMap<V> {

private final Environment environment;

Expand Down Expand Up @@ -73,37 +71,29 @@ public <T> T get(CharSequence name, Class<T> requiredType, T defaultValue) {
@Override
public <T> Optional<T> get(CharSequence name, ArgumentConversionContext<T> conversionContext) {
V value = map.get(name);
if (value instanceof AnnotationClassValue) {
AnnotationClassValue acv = (AnnotationClassValue) value;
if (value instanceof AnnotationClassValue<?> acv) {
return environment.convert(acv, conversionContext);
} else if (value instanceof CharSequence) {
PropertyPlaceholderResolver placeholderResolver = environment.getPlaceholderResolver();
String str = doResolveIfNecessary((CharSequence) value, placeholderResolver);
return environment.convert(str, conversionContext);
} else if (value instanceof String[]) {
} else if (value instanceof String[] values) {
PropertyPlaceholderResolver placeholderResolver = environment.getPlaceholderResolver();
String[] resolved = Arrays.stream((String[]) value)
.flatMap(val -> {
try {
String[] values = placeholderResolver.resolveRequiredPlaceholder(val, String[].class);
return Arrays.stream(values);
} catch (ConfigurationException e) {
return Stream.of(doResolveIfNecessary(val, placeholderResolver));
}
})
String[] resolved = Arrays.stream(values)
.flatMap(val -> placeholderResolver.resolveOptionalPlaceholder(val, String[].class)
.map(Arrays::stream)
.orElseGet(() -> Stream.of(doResolveIfNecessary(val, placeholderResolver))))
.toArray(String[]::new);
return environment.convert(resolved, conversionContext);
} else if (value instanceof io.micronaut.core.annotation.AnnotationValue[]) {
io.micronaut.core.annotation.AnnotationValue[] annotationValues = (io.micronaut.core.annotation.AnnotationValue[]) value;
io.micronaut.core.annotation.AnnotationValue[] b = new AnnotationValue[annotationValues.length];
} else if (value instanceof AnnotationValue<?>[] annotationValues) {
io.micronaut.core.annotation.AnnotationValue<?>[] b = new AnnotationValue[annotationValues.length];
for (int i = 0; i < annotationValues.length; i++) {
io.micronaut.core.annotation.AnnotationValue annotationValue = annotationValues[i];
b[i] = new EnvironmentAnnotationValue(environment, annotationValue);
io.micronaut.core.annotation.AnnotationValue<?> annotationValue = annotationValues[i];
b[i] = new EnvironmentAnnotationValue<>(environment, annotationValue);
}
return environment.convert(b, conversionContext);
} else if (value instanceof io.micronaut.core.annotation.AnnotationValue) {
io.micronaut.core.annotation.AnnotationValue av = (io.micronaut.core.annotation.AnnotationValue) value;
av = new EnvironmentAnnotationValue(environment, av);
} else if (value instanceof AnnotationValue<?> av) {
av = new EnvironmentAnnotationValue<>(environment, av);
return environment.convert(av, conversionContext);
} else {
return super.get(name, conversionContext);
Expand All @@ -118,7 +108,7 @@ public Collection<V> values() {
v = (V) environment.getPlaceholderResolver().resolveRequiredPlaceholders(v.toString());
}
return v;
}).collect(Collectors.toList());
}).toList();
}

private String doResolveIfNecessary(CharSequence value, PropertyPlaceholderResolver placeholderResolver) {
Expand Down

0 comments on commit b9b93dd

Please sign in to comment.