diff --git a/mycore-base/src/main/java/org/mycore/common/config/MCRConfiguration2.java b/mycore-base/src/main/java/org/mycore/common/config/MCRConfiguration2.java index 4ee8bd1048..c2efb0d674 100644 --- a/mycore-base/src/main/java/org/mycore/common/config/MCRConfiguration2.java +++ b/mycore-base/src/main/java/org/mycore/common/config/MCRConfiguration2.java @@ -26,7 +26,6 @@ import java.util.UUID; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; import java.util.function.Predicate; @@ -35,18 +34,15 @@ import java.util.stream.Stream; import org.mycore.common.MCRClassTools; -import org.mycore.common.MCRException; +import org.mycore.common.config.instantiator.MCREmptyTree; import org.mycore.common.config.instantiator.MCRInstanceConfiguration; import org.mycore.common.config.instantiator.MCRInstanceConfiguration.Options; import org.mycore.common.config.instantiator.MCRInstanceName; import org.mycore.common.config.instantiator.MCRInstantiatorUtils; +import org.mycore.common.config.instantiator.MCRProperTree; +import org.mycore.common.config.instantiator.MCRPropertiesTree; import org.mycore.common.function.MCRTriConsumer; -import com.google.common.base.Throwables; -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; -import com.google.common.util.concurrent.UncheckedExecutionException; - import jakarta.inject.Singleton; /** @@ -101,15 +97,10 @@ public class MCRConfiguration2 { private static final Map LISTENERS = new ConcurrentHashMap<>(); - private static final int CONFIGURATIONS_CACHE_SIZE = 32; - - private static final Cache> CONFIGURATIONS = CacheBuilder.newBuilder() - .maximumSize(CONFIGURATIONS_CACHE_SIZE) - .softValues() - .build(); - private static final AtomicReference> PROPERTIES = new AtomicReference<>(Map.of()); + private static final AtomicReference PROPER_TREE = new AtomicReference<>(MCRPropertiesTree.EMPTY); + static Map instanceHolder = new MCRConcurrentHashMap<>(); /** @@ -139,6 +130,19 @@ public static Map getAllPropertiesMap() { }); } + /** + * Returns all configuration properties, excluding properties with blank values, i.e. + * only properties that are recognized by, for example, {@link #getString(String)} as a tree. + */ + public static MCRProperTree getAllPropertiesTree() { + return PROPER_TREE.updateAndGet(existingValue -> { + if (existingValue instanceof MCREmptyTree) { + return MCRPropertiesTree.ofProperties(getAllPropertiesMap()); + } + return existingValue; + }); + } + /** * @deprecated Use {@link MCRConfigurationBase#getSubpropertiesMap(String)} as one-to-one-replacement * or {@link #getSubpropertiesMap(String)} as an alternative version that is better aligned with @@ -257,16 +261,8 @@ private static Optional createSingleInstanceOf(MCRInstanceConfiguration configuration.instantiate())); } - @SuppressWarnings({ "unchecked", "PMD.PreserveStackTrace" }) private static MCRInstanceConfiguration getConfiguration(Class superClass, String name) { - try { - return ((MCRInstanceConfiguration) CONFIGURATIONS.get( - new ConfigurationKey(superClass.getName(), name), - () -> MCRInstanceConfiguration.ofName(superClass, name, Options.IMPLICIT))).copy(); - } catch (ExecutionException | UncheckedExecutionException e) { - Throwables.throwIfUnchecked(e.getCause()); - throw new MCRException("Failed to create instance configuration for " + name, e.getCause()); - } + return MCRInstanceConfiguration.ofName(superClass, name, getAllPropertiesTree(), Options.IMPLICIT); } /** @@ -508,8 +504,8 @@ public static boolean removePropertyChangeEventListener(UUID uuid) { } public static S instantiateClass(Class superClass, String className) { - return MCRInstanceConfiguration.ofClassName(superClass, className, "MCR.AnonymousInstance." + className) - .instantiate(); + return MCRInstanceConfiguration.ofClassName(superClass, className, "MCR.AnonymousInstance." + className, + getAllPropertiesTree()).instantiate(); } public static Stream instantiateClasses(Class superClass, String propertyName) { @@ -528,10 +524,10 @@ private static Class getClassObject(String classname) { } static void clearCaches() { - // BEWARE: do NOT replace with constant value, i.e. do NOT replace with PROPERTIES.set(Map.of()); + // BEWARE: do NOT replace with constant values, i.e. do NOT replace with PROPERTIES.set(Map.of()); // AtomicReference#updateAndGet only works as expected, when each update sets a distinct value PROPERTIES.set(new HashMap<>()); - CONFIGURATIONS.invalidateAll(); + PROPER_TREE.set(new MCREmptyTree()); } private static class EventListener { @@ -560,7 +556,4 @@ interface SingletonKey { record ConfigSingletonKey(String property, String classname) implements SingletonKey { } - record ConfigurationKey(String superclassName, String name) { - } - } diff --git a/mycore-base/src/main/java/org/mycore/common/config/instantiator/MCREmptyTree.java b/mycore-base/src/main/java/org/mycore/common/config/instantiator/MCREmptyTree.java new file mode 100644 index 0000000000..31fad2f988 --- /dev/null +++ b/mycore-base/src/main/java/org/mycore/common/config/instantiator/MCREmptyTree.java @@ -0,0 +1,50 @@ +package org.mycore.common.config.instantiator; + +import java.util.Map; +import java.util.stream.Stream; + +import org.mycore.common.log.MCRTreeMessage; + +public class MCREmptyTree implements MCRProperTree { + + @Override + public String value() { + return ""; + } + + @Override + public Stream keys() { + return Stream.empty(); + } + + @Override + public MCRProperTree nested(String key) { + return MCRPropertiesTree.EMPTY; + } + + @Override + public MCRProperTree deeplyNested(String key) { + return MCRPropertiesTree.EMPTY; + } + + @Override + public MCRProperTree excludeKey(String classKey) { + return this; + } + + @Override + public Map toProperties() { + return Map.of(); + } + + @Override + public MCRTreeMessage toTreeMessage() { + return new MCRTreeMessage(); + } + + @Override + public String toString() { + return "{ }"; + } + +} diff --git a/mycore-base/src/main/java/org/mycore/common/config/instantiator/MCRInstanceConfiguration.java b/mycore-base/src/main/java/org/mycore/common/config/instantiator/MCRInstanceConfiguration.java index 2cd28b88b4..42d3edd823 100644 --- a/mycore-base/src/main/java/org/mycore/common/config/instantiator/MCRInstanceConfiguration.java +++ b/mycore-base/src/main/java/org/mycore/common/config/instantiator/MCRInstanceConfiguration.java @@ -19,8 +19,6 @@ package org.mycore.common.config.instantiator; import java.lang.reflect.Modifier; -import java.util.HashMap; -import java.util.Map; import java.util.Set; import org.apache.logging.log4j.LogManager; @@ -31,8 +29,8 @@ import org.mycore.common.config.MCRConfigurationException; /** - * Represents an extract of properties (obtained via {@link MCRConfiguration2#getAllPropertiesMap()}) used to - * instantiate an object. Provides methods to extract nested configurations. + * Represents an extract of properties (typically {@link MCRConfiguration2#getAllPropertiesTree()}) used to + * instantiate an object. *

* Generally speaking, a configuration has a {@link MCRInstanceConfiguration#name()} that represents the * property key used to convey the {@link MCRInstanceConfiguration#valueClass()} of the class that should @@ -60,18 +58,17 @@ public final class MCRInstanceConfiguration { private final MCRInstanceName name; - private final Map properties; + private final MCRProperTree properties; - private final Map fullProperties; + private final MCRProperTree fullProperties; private MCRInstanceConfiguration(Class superClass, Class valueClass, MCRInstanceName name, - Map properties, Map fullProperties) { + MCRProperTree properties, MCRProperTree fullProperties) { this.superClass = superClass; this.valueClass = valueClass; this.name = name; - this.properties = properties; + this.properties = properties.excludeKey(CLASS_KEY); this.fullProperties = fullProperties; - properties.remove(CLASS_KEY); } public boolean instantiatable() { @@ -83,21 +80,30 @@ public S instantiate() { } /** - * Shorthand for {@link MCRInstanceConfiguration#ofClass(Class, Class, String)} that + * Shorthand for {@link MCRInstanceConfiguration#ofClass(Class, Class, String, MCRProperTree)} that * uses {@link MCRClassTools#forName(String)} to resolve the value class. */ public static MCRInstanceConfiguration ofClassName(Class superClass, String className, - String prefix) { + String prefix, MCRProperTree properties) { try { Class valueClass = MCRClassTools.forName(className); - return ofClass(superClass, valueClass, prefix); + return ofClass(superClass, valueClass, prefix, properties); } catch (ClassNotFoundException | LinkageError cause) { throw new MCRException("Failed to load class (" + className + ")", cause); } } /** - * Creates a new configuration for the given super class and value class. + * Shorthand for {@link MCRInstanceConfiguration#ofClass(Class, Class, String, MCRProperTree)} that + * uses {@link MCRConfiguration2#getAllPropertiesTree()} to obtain the properties. + */ + public static MCRInstanceConfiguration ofClass(Class superClass, Class valueClass, + String prefix) { + return ofClass(superClass, valueClass, prefix, MCRConfiguration2.getAllPropertiesTree()); + } + + /** + * Creates a new configuration for the given super class and value class based on the given properties. *

* Example: Given value class Some.Instance.Name, * prefix Some.Instance.Name and @@ -114,34 +120,55 @@ public static MCRInstanceConfiguration ofClassName(Class superClass, S *

  • Key1=Value1
  • *
  • Key2=Value2
  • * + * and {@link MCRInstanceConfiguration#fullProperties()} that are equal to the given properties. */ public static MCRInstanceConfiguration ofClass(Class superClass, Class valueClass, - String prefix) { + String prefix, MCRProperTree properties) { MCRInstanceName name = MCRInstanceName.of(prefix); - Map properties = MCRConfiguration2.getAllPropertiesMap(); - Map reducedProperties = reduceProperties(prefix, properties); - return new MCRInstanceConfiguration<>(superClass, valueClass, name, reducedProperties, properties); + MCRProperTree nestedProperties = properties.deeplyNested(prefix); + return new MCRInstanceConfiguration<>(superClass, valueClass, name, nestedProperties, properties); } /** - * Shorthand for {@link MCRInstanceConfiguration#ofName(Class, MCRInstanceName, Set)} that + * Shorthand for {@link MCRInstanceConfiguration#ofName(Class, MCRInstanceName, MCRProperTree, Set)} that * creates the name with {@link MCRInstanceName#of(String)} and + * uses {@link MCRConfiguration2#getAllPropertiesTree()} to obtain the properties. * uses {@link Options#NONE} as the options. */ public static MCRInstanceConfiguration ofName(Class superClass, String name) { - return ofName(superClass, MCRInstanceName.of(name), Options.NONE); + return ofName(superClass, MCRInstanceName.of(name), MCRConfiguration2.getAllPropertiesTree(), Options.NONE); } /** - * Shorthand for {@link MCRInstanceConfiguration#ofName(Class, MCRInstanceName, Set)} that - * creates the name with {@link MCRInstanceName#of(String)}. + * Shorthand for {@link MCRInstanceConfiguration#ofName(Class, MCRInstanceName, MCRProperTree, Set)} that + * creates the name with {@link MCRInstanceName#of(String)} and + * uses {@link Options#NONE} as the options. + */ + public static MCRInstanceConfiguration ofName(Class superClass, String name, + MCRProperTree properties) { + return ofName(superClass, MCRInstanceName.of(name), properties, Options.NONE); + } + + /** + * Shorthand for {@link MCRInstanceConfiguration#ofName(Class, MCRInstanceName, MCRProperTree, Set)} that + * creates the name with {@link MCRInstanceName#of(String)} and + * uses {@link MCRConfiguration2#getAllPropertiesTree()} to obtain the properties. */ public static MCRInstanceConfiguration ofName(Class superClass, String name, Set