From db94000e3f81a2a691f2ae006b500c06034cc1e8 Mon Sep 17 00:00:00 2001 From: "ah.jo" Date: Fri, 16 Jun 2023 11:39:05 +0900 Subject: [PATCH] Deprecate PropertyCache (#676) --- .../api/collection/IteratorCache.java | 2 +- .../api/collection/StreamCache.java | 2 +- .../api/context/MonkeyContextBuilder.java | 2 +- .../BuilderArbitraryIntrospector.java | 12 +- .../api/property/PropertyCache.java | 158 +++--------------- .../fixturemonkey/api/type/TypeCache.java | 150 ++++++++++++++++- ...PrimaryConstructorArbitraryIntrospector.kt | 2 +- .../kotlin/property/KotlinPropertyCache.kt | 2 +- .../property/KotlinPropertyGenerator.kt | 5 +- 9 files changed, 186 insertions(+), 149 deletions(-) diff --git a/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/collection/IteratorCache.java b/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/collection/IteratorCache.java index fbf2843f1..48127f463 100644 --- a/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/collection/IteratorCache.java +++ b/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/collection/IteratorCache.java @@ -27,7 +27,7 @@ @API(since = "0.4.0", status = Status.MAINTAINED) public final class IteratorCache { - private static final LruCache, List> ITERATOR_TO_LIST = new LruCache<>(2000); + private static final LruCache, List> ITERATOR_TO_LIST = new LruCache<>(2048); public static List getList(Iterator iterator) { if (ITERATOR_TO_LIST.containsKey(iterator)) { diff --git a/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/collection/StreamCache.java b/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/collection/StreamCache.java index 8eb4405c4..2e929d4c2 100644 --- a/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/collection/StreamCache.java +++ b/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/collection/StreamCache.java @@ -27,7 +27,7 @@ @API(since = "0.4.0", status = Status.MAINTAINED) public final class StreamCache { - private static final LruCache, List> STREAM_TO_LIST = new LruCache<>(2000); + private static final LruCache, List> STREAM_TO_LIST = new LruCache<>(2048); public static List getList(Stream stream) { if (STREAM_TO_LIST.containsKey(stream)) { diff --git a/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/context/MonkeyContextBuilder.java b/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/context/MonkeyContextBuilder.java index 256b34b51..b43d7c3c9 100644 --- a/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/context/MonkeyContextBuilder.java +++ b/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/context/MonkeyContextBuilder.java @@ -30,7 +30,7 @@ public final class MonkeyContextBuilder { private LruCache arbitrariesByProperty; private LruCache generatorContextByRootProperty; - private int cacheSize = 2000; + private int cacheSize = 2048; private int generatorContextSize = 1000; public MonkeyContextBuilder arbitrariesByProperty( diff --git a/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/introspector/BuilderArbitraryIntrospector.java b/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/introspector/BuilderArbitraryIntrospector.java index 2df3d1b2b..8e70ea89c 100644 --- a/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/introspector/BuilderArbitraryIntrospector.java +++ b/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/introspector/BuilderArbitraryIntrospector.java @@ -44,13 +44,13 @@ @API(since = "0.4.0", status = API.Status.EXPERIMENTAL) public final class BuilderArbitraryIntrospector implements ArbitraryIntrospector { public static final BuilderArbitraryIntrospector INSTANCE = new BuilderArbitraryIntrospector(); - private static final Map, Method> BUILDER_CACHE = new ConcurrentHashMap<>(2000); - private static final Map BUILD_FIELD_METHOD_CACHE = new ConcurrentHashMap<>(2000); - private static final Map, Method> BUILD_METHOD_CACHE = new ConcurrentHashMap<>(2000); - private static final Map, Class> BUILDER_TYPE_CACHE = new ConcurrentHashMap<>(2000); + private static final Map, Method> BUILDER_CACHE = new ConcurrentHashMap<>(2048); + private static final Map BUILD_FIELD_METHOD_CACHE = new ConcurrentHashMap<>(2048); + private static final Map, Method> BUILD_METHOD_CACHE = new ConcurrentHashMap<>(2048); + private static final Map, Class> BUILDER_TYPE_CACHE = new ConcurrentHashMap<>(2048); - private final Map, String> typedBuilderMethodName = new ConcurrentHashMap<>(2000); - private final Map, String> typedBuildMethodName = new ConcurrentHashMap<>(2000); + private final Map, String> typedBuilderMethodName = new ConcurrentHashMap<>(2048); + private final Map, String> typedBuildMethodName = new ConcurrentHashMap<>(2048); private String defaultBuildMethodName = "build"; private String defaultBuilderMethodName = "builder"; diff --git a/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/property/PropertyCache.java b/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/property/PropertyCache.java index c9c2d3a7d..d83009d5e 100644 --- a/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/property/PropertyCache.java +++ b/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/property/PropertyCache.java @@ -18,52 +18,30 @@ package com.navercorp.fixturemonkey.api.property; -import static java.util.stream.Collectors.toList; - -import java.beans.ConstructorProperties; -import java.beans.IntrospectionException; -import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.AnnotatedArrayType; import java.lang.reflect.AnnotatedType; import java.lang.reflect.AnnotatedTypeVariable; import java.lang.reflect.Constructor; import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.lang.reflect.Parameter; -import java.util.AbstractMap.SimpleEntry; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; import javax.annotation.Nullable; import org.apiguardian.api.API; import org.apiguardian.api.API.Status; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import com.navercorp.fixturemonkey.api.collection.LruCache; import com.navercorp.fixturemonkey.api.generator.DefaultPropertyGenerator; -import com.navercorp.fixturemonkey.api.type.Reflections; +import com.navercorp.fixturemonkey.api.type.TypeCache; import com.navercorp.fixturemonkey.api.type.Types; @API(since = "0.4.0", status = Status.MAINTAINED) public final class PropertyCache { - private static final Logger LOGGER = LoggerFactory.getLogger(PropertyCache.class); - - private static final Map, Map> PROPERTY_DESCRIPTORS = - new LruCache<>(2000); - private static final Map, Map> FIELDS = new LruCache<>(2000); - private static final Map, Map.Entry, String[]>> PARAMETER_NAMES_BY_PRIMARY_CONSTRUCTOR = - new LruCache<>(2000); - @Deprecated // It would be removed when getProperties is removed. private static final DefaultPropertyGenerator DEFAULT_PROPERTY_GENERATOR = new DefaultPropertyGenerator(); @@ -84,25 +62,13 @@ public static Map getFields(Class clazz) { return getFieldsByName(Types.generateAnnotatedTypeWithoutAnnotation(clazz)); } + /** + * It is Deprecated. Use {@link TypeCache#getFieldsByName(Class)} instead. + */ + @Deprecated public static Map getFieldsByName(AnnotatedType annotatedType) { Class clazz = Types.getActualType(annotatedType.getType()); - - return FIELDS.computeIfAbsent(clazz, type -> { - Map result = new ConcurrentHashMap<>(); - try { - List fields = Reflections.findFields(clazz) - .stream() - .filter(it -> !Modifier.isStatic(it.getModifiers())) - .collect(toList()); - for (Field field : fields) { - field.setAccessible(true); - result.put(field.getName(), field); - } - } catch (Exception e) { - LOGGER.warn("Failed to create fields in type {}.", clazz.getName()); - } - return result; - }); + return TypeCache.getFieldsByName(clazz); } @Deprecated // It would be removed in 0.6.0 @@ -110,25 +76,13 @@ public static Map getPropertyDescriptors(Class cl return getPropertyDescriptorsByPropertyName(Types.generateAnnotatedTypeWithoutAnnotation(clazz)); } + /** + * It is Deprecated. Use {@link TypeCache#getPropertyDescriptorsByPropertyName(Class)} instead. + */ + @Deprecated public static Map getPropertyDescriptorsByPropertyName(AnnotatedType annotatedType) { Class clazz = Types.getActualType(annotatedType.getType()); - - return PROPERTY_DESCRIPTORS.computeIfAbsent(clazz, type -> { - Map result = new ConcurrentHashMap<>(); - try { - PropertyDescriptor[] descriptors = Introspector.getBeanInfo(type) - .getPropertyDescriptors(); - for (PropertyDescriptor descriptor : descriptors) { - if (descriptor.getName().equals("class")) { - continue; - } - result.put(descriptor.getName(), descriptor); - } - } catch (IntrospectionException ex) { - LOGGER.warn("Introspect bean property is failed. type: " + clazz, ex); - } - return result; - }); + return TypeCache.getPropertyDescriptorsByPropertyName(clazz); } @Deprecated // It would be removed in 0.6.0 @@ -136,6 +90,10 @@ public static Map getConstructorProperties(AnnotatedType annot return getConstructorParameterPropertiesByParameterName(annotatedType); } + /** + * It is Deprecated. Use {@link ConstructorParameterPropertyGenerator} instead. + */ + @Deprecated public static Map getConstructorParameterPropertiesByParameterName(AnnotatedType annotatedType) { Class clazz = Types.getActualType(annotatedType.getType()); @@ -187,87 +145,21 @@ public static Map getConstructorParameterPropertiesByParameter return Collections.unmodifiableMap(constructorPropertiesByName); } + /** + * It is Deprecated. Use {@link TypeCache#getParameterNamesByConstructor(Class)}} instead. + */ + @Deprecated @Nullable public static Entry, String[]> getParameterNamesByConstructor(Class clazz) { - return PARAMETER_NAMES_BY_PRIMARY_CONSTRUCTOR.computeIfAbsent(clazz, - type -> { - List> possibilities = new ArrayList<>(); - - Constructor[] constructors = clazz.getDeclaredConstructors(); - - for (Constructor constructor : constructors) { - Parameter[] parameters = constructor.getParameters(); - boolean namePresent = Arrays.stream(parameters).anyMatch(Parameter::isNamePresent); - boolean parameterEmpty = parameters.length == 0; - if (namePresent || parameterEmpty) { - possibilities.add(constructor); - } else { - ConstructorProperties constructorPropertiesAnnotation = - constructor.getAnnotation(ConstructorProperties.class); - - if (constructorPropertiesAnnotation != null) { - possibilities.add(constructor); - } - } - } - - boolean constructorPropertiesPresent = possibilities.stream() - .anyMatch(it -> it.getAnnotation(ConstructorProperties.class) != null); - - Constructor primaryConstructor; - if (constructorPropertiesPresent) { - primaryConstructor = possibilities.stream() - .filter(it -> it.getAnnotation(ConstructorProperties.class) != null) - .findFirst() - .orElseThrow(() -> new IllegalArgumentException( - "Constructor should have @ConstructorProperties" + clazz.getSimpleName()) - ); - } else { - primaryConstructor = possibilities.stream() - .findFirst() - .orElse(null); - } - - if (primaryConstructor == null) { - return null; - } - - String[] parameterNames = getParameterNames(primaryConstructor); - AnnotatedType[] annotatedParameterTypes = primaryConstructor.getAnnotatedParameterTypes(); - - if (parameterNames.length != annotatedParameterTypes.length) { - throw new IllegalArgumentException( - "@ConstructorProperties values size should same as constructor parameter size" - ); - } - return new SimpleEntry<>(primaryConstructor, parameterNames); - }); + return TypeCache.getParameterNamesByConstructor(clazz); } + /** + * It is Deprecated. Use {@link TypeCache#clearCache()} instead. + */ + @Deprecated public static void clearCache() { - PARAMETER_NAMES_BY_PRIMARY_CONSTRUCTOR.clear(); - PROPERTY_DESCRIPTORS.clear(); - FIELDS.clear(); - } - - private static String[] getParameterNames(Constructor constructor) { - Parameter[] parameters = constructor.getParameters(); - boolean namePresent = Arrays.stream(parameters).anyMatch(Parameter::isNamePresent); - boolean parameterEmpty = parameters.length == 0; - - if (parameterEmpty) { - return new String[0]; - } - - if (namePresent) { - return Arrays.stream(parameters) - .map(Parameter::getName) - .toArray(String[]::new); - } else { - ConstructorProperties constructorPropertiesAnnotation = - constructor.getAnnotation(ConstructorProperties.class); - return constructorPropertiesAnnotation.value(); - } + TypeCache.clearCache(); } private static boolean isGenericAnnotatedType(AnnotatedType annotatedType) { diff --git a/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/type/TypeCache.java b/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/type/TypeCache.java index fec0ffcd9..cca2a186f 100644 --- a/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/type/TypeCache.java +++ b/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/type/TypeCache.java @@ -18,20 +18,45 @@ package com.navercorp.fixturemonkey.api.type; +import static java.util.stream.Collectors.toList; + +import java.beans.ConstructorProperties; +import java.beans.IntrospectionException; +import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.AnnotatedType; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.lang.reflect.Parameter; +import java.util.AbstractMap.SimpleEntry; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; +import javax.annotation.Nullable; + import org.apiguardian.api.API; import org.apiguardian.api.API.Status; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.navercorp.fixturemonkey.api.collection.LruCache; @API(since = "0.4.0", status = Status.MAINTAINED) public final class TypeCache { - private static final Map FIELD_ANNOTATED_TYPE_MAP = new ConcurrentHashMap<>(2000); + private static final Logger LOGGER = LoggerFactory.getLogger(TypeCache.class); + private static final Map FIELD_ANNOTATED_TYPE_MAP = new ConcurrentHashMap<>(2048); private static final Map PROPERTY_DESCRIPTOR_ANNOTATED_TYPE_MAP = - new ConcurrentHashMap<>(2000); + new ConcurrentHashMap<>(2048); + private static final Map, Map> PROPERTY_DESCRIPTORS = + new LruCache<>(2048); + private static final Map, Map> FIELDS = new LruCache<>(2048); + private static final Map, Map.Entry, String[]>> PARAMETER_NAMES_BY_PRIMARY_CONSTRUCTOR = + new LruCache<>(2048); public static AnnotatedType getAnnotatedType(Field field) { return FIELD_ANNOTATED_TYPE_MAP.computeIfAbsent(field, Field::getAnnotatedType); @@ -43,4 +68,125 @@ public static AnnotatedType getAnnotatedType(PropertyDescriptor propertyDescript it -> it.getReadMethod().getAnnotatedReturnType() ); } + + public static Map getFieldsByName(Class clazz) { + return FIELDS.computeIfAbsent(clazz, type -> { + Map result = new ConcurrentHashMap<>(); + try { + List fields = Reflections.findFields(clazz) + .stream() + .filter(it -> !Modifier.isStatic(it.getModifiers())) + .collect(toList()); + for (Field field : fields) { + field.setAccessible(true); + result.put(field.getName(), field); + } + } catch (Exception e) { + LOGGER.warn("Failed to create fields in type {}.", clazz.getName()); + } + return result; + }); + } + + public static Map getPropertyDescriptorsByPropertyName(Class clazz) { + return PROPERTY_DESCRIPTORS.computeIfAbsent(clazz, type -> { + Map result = new ConcurrentHashMap<>(); + try { + PropertyDescriptor[] descriptors = Introspector.getBeanInfo(type) + .getPropertyDescriptors(); + for (PropertyDescriptor descriptor : descriptors) { + if (descriptor.getName().equals("class")) { + continue; + } + result.put(descriptor.getName(), descriptor); + } + } catch (IntrospectionException ex) { + LOGGER.warn("Introspect bean property is failed. type: " + clazz, ex); + } + return result; + }); + } + + @Nullable + public static Entry, String[]> getParameterNamesByConstructor(Class clazz) { + return PARAMETER_NAMES_BY_PRIMARY_CONSTRUCTOR.computeIfAbsent(clazz, + type -> { + List> possibilities = new ArrayList<>(); + + Constructor[] constructors = clazz.getDeclaredConstructors(); + + for (Constructor constructor : constructors) { + Parameter[] parameters = constructor.getParameters(); + boolean namePresent = Arrays.stream(parameters).anyMatch(Parameter::isNamePresent); + boolean parameterEmpty = parameters.length == 0; + if (namePresent || parameterEmpty) { + possibilities.add(constructor); + } else { + ConstructorProperties constructorPropertiesAnnotation = + constructor.getAnnotation(ConstructorProperties.class); + + if (constructorPropertiesAnnotation != null) { + possibilities.add(constructor); + } + } + } + + boolean constructorPropertiesPresent = possibilities.stream() + .anyMatch(it -> it.getAnnotation(ConstructorProperties.class) != null); + + Constructor primaryConstructor; + if (constructorPropertiesPresent) { + primaryConstructor = possibilities.stream() + .filter(it -> it.getAnnotation(ConstructorProperties.class) != null) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException( + "Constructor should have @ConstructorProperties" + clazz.getSimpleName()) + ); + } else { + primaryConstructor = possibilities.stream() + .findFirst() + .orElse(null); + } + + if (primaryConstructor == null) { + return null; + } + + String[] parameterNames = getParameterNames(primaryConstructor); + AnnotatedType[] annotatedParameterTypes = primaryConstructor.getAnnotatedParameterTypes(); + + if (parameterNames.length != annotatedParameterTypes.length) { + throw new IllegalArgumentException( + "@ConstructorProperties values size should same as constructor parameter size" + ); + } + return new SimpleEntry<>(primaryConstructor, parameterNames); + }); + } + + public static void clearCache() { + PARAMETER_NAMES_BY_PRIMARY_CONSTRUCTOR.clear(); + PROPERTY_DESCRIPTORS.clear(); + FIELDS.clear(); + } + + private static String[] getParameterNames(Constructor constructor) { + Parameter[] parameters = constructor.getParameters(); + boolean namePresent = Arrays.stream(parameters).anyMatch(Parameter::isNamePresent); + boolean parameterEmpty = parameters.length == 0; + + if (parameterEmpty) { + return new String[0]; + } + + if (namePresent) { + return Arrays.stream(parameters) + .map(Parameter::getName) + .toArray(String[]::new); + } else { + ConstructorProperties constructorPropertiesAnnotation = + constructor.getAnnotation(ConstructorProperties.class); + return constructorPropertiesAnnotation.value(); + } + } } diff --git a/fixture-monkey-kotlin/src/main/kotlin/com/navercorp/fixturemonkey/kotlin/introspector/PrimaryConstructorArbitraryIntrospector.kt b/fixture-monkey-kotlin/src/main/kotlin/com/navercorp/fixturemonkey/kotlin/introspector/PrimaryConstructorArbitraryIntrospector.kt index d01e3ab20..119151cca 100644 --- a/fixture-monkey-kotlin/src/main/kotlin/com/navercorp/fixturemonkey/kotlin/introspector/PrimaryConstructorArbitraryIntrospector.kt +++ b/fixture-monkey-kotlin/src/main/kotlin/com/navercorp/fixturemonkey/kotlin/introspector/PrimaryConstructorArbitraryIntrospector.kt @@ -38,7 +38,7 @@ import kotlin.reflect.full.primaryConstructor class PrimaryConstructorArbitraryIntrospector : ArbitraryIntrospector { companion object { val INSTANCE = PrimaryConstructorArbitraryIntrospector() - private val CONSTRUCTOR_CACHE = LruCache, KFunction<*>>(2000) + private val CONSTRUCTOR_CACHE = LruCache, KFunction<*>>(2048) } override fun introspect(context: ArbitraryGeneratorContext): ArbitraryIntrospectorResult { diff --git a/fixture-monkey-kotlin/src/main/kotlin/com/navercorp/fixturemonkey/kotlin/property/KotlinPropertyCache.kt b/fixture-monkey-kotlin/src/main/kotlin/com/navercorp/fixturemonkey/kotlin/property/KotlinPropertyCache.kt index 7d5829f00..6d439ac9d 100644 --- a/fixture-monkey-kotlin/src/main/kotlin/com/navercorp/fixturemonkey/kotlin/property/KotlinPropertyCache.kt +++ b/fixture-monkey-kotlin/src/main/kotlin/com/navercorp/fixturemonkey/kotlin/property/KotlinPropertyCache.kt @@ -27,7 +27,7 @@ import java.lang.reflect.AnnotatedType import kotlin.reflect.KProperty import kotlin.reflect.full.memberProperties -private val KPROPERTY_ANNOTATED_TYPE_MAP = LruCache, Collection>>(2000) +private val KPROPERTY_ANNOTATED_TYPE_MAP = LruCache, Collection>>(2048) @API(since = "0.4.0", status = API.Status.EXPERIMENTAL) fun getMemberProperties(annotatedType: AnnotatedType): List { diff --git a/fixture-monkey-kotlin/src/main/kotlin/com/navercorp/fixturemonkey/kotlin/property/KotlinPropertyGenerator.kt b/fixture-monkey-kotlin/src/main/kotlin/com/navercorp/fixturemonkey/kotlin/property/KotlinPropertyGenerator.kt index 1b83f47d5..35f4228a6 100644 --- a/fixture-monkey-kotlin/src/main/kotlin/com/navercorp/fixturemonkey/kotlin/property/KotlinPropertyGenerator.kt +++ b/fixture-monkey-kotlin/src/main/kotlin/com/navercorp/fixturemonkey/kotlin/property/KotlinPropertyGenerator.kt @@ -23,7 +23,6 @@ import com.navercorp.fixturemonkey.api.generator.DefaultPropertyGenerator import com.navercorp.fixturemonkey.api.property.CompositeProperty import com.navercorp.fixturemonkey.api.property.Property import com.navercorp.fixturemonkey.api.property.PropertyGenerator -import com.navercorp.fixturemonkey.api.type.Types import org.apiguardian.api.API import java.lang.reflect.AnnotatedType @@ -37,10 +36,10 @@ class KotlinPropertyGenerator( private val javaDelegatePropertyGenerator: com.navercorp.fixturemonkey.api.generator.PropertyGenerator = DefaultPropertyGenerator(), ) : PropertyGenerator { - private val objectChildPropertiesCache = LruCache, List>(2000) + private val objectChildPropertiesCache = LruCache>(2048) override fun generateChildProperties(annotatedType: AnnotatedType): List = - objectChildPropertiesCache.computeIfAbsent(Types.getActualType(annotatedType.type)) { + objectChildPropertiesCache.computeIfAbsent(annotatedType) { val javaProperties = javaDelegatePropertyGenerator.generateChildProperties(annotatedType) .filter { it.name != null } .associateBy { it.name!! }