From 9af36971677600df05597dd418f3962f9a55d1c6 Mon Sep 17 00:00:00 2001 From: Torsten Krause Date: Mon, 27 Jul 2026 21:57:35 +0200 Subject: [PATCH 1/4] MCR-3703 ! parse configuration properties into tree structure eagerly --- .../common/config/MCRConfiguration2.java | 46 ++--- .../MCRInstanceConfiguration.java | 116 +++++------ .../config/instantiator/MCRInstantiator.java | 2 - .../config/instantiator/MCRProperTree.java | 185 ++++++++++++++++++ .../source/MCRClassPropertyExtractor.java | 8 +- .../source/MCRInstanceExtractor.java | 6 +- .../source/MCRPropertyExtractor.java | 7 +- .../source/MCRRawPropertiesSource.java | 27 ++- .../instantiator/source/MCRSourceBase.java | 58 +----- .../instantiator/source/MCRSourceContext.java | 22 --- .../source/MCRValueExtractor.java | 4 +- .../source/MCRValueListSourceBase.java | 36 +++- .../source/MCRValueMapSourceBase.java | 15 +- .../source/MCRValueSourceBase.java | 7 +- .../org/mycore/common/log/MCRTreeMessage.java | 13 +- .../frontend/cli/MCRDeveloperCommands.java | 25 ++- .../common/config/MCRConfigurationTest.java | 2 +- .../config/MCRInstanceConfigurationTest.java | 24 +-- .../config/MCRInstantiatorBasicTest.java | 27 ++- ...MCRInstantiatorClassPropertyBasicTest.java | 27 +-- .../config/MCRInstantiatorCollectionTest.java | 75 +++---- .../config/MCRInstantiatorConfigTest.java | 1 + .../config/MCRInstantiatorDefaultTest.java | 27 ++- .../MCRInstantiatorInstanceBasicTest.java | 13 +- .../config/MCRInstantiatorNestedTest.java | 80 +++----- .../config/MCRInstantiatorProxyTest.java | 23 ++- ...MCRInstantiatorRawPropertiesBasicTest.java | 73 +++++++ 27 files changed, 583 insertions(+), 366 deletions(-) create mode 100644 mycore-base/src/main/java/org/mycore/common/config/instantiator/MCRProperTree.java create mode 100644 mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorRawPropertiesBasicTest.java 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..249637542a 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,13 @@ import java.util.stream.Stream; import org.mycore.common.MCRClassTools; -import org.mycore.common.MCRException; 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.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 +95,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<>(MCRProperTree.empty()); + static Map instanceHolder = new MCRConcurrentHashMap<>(); /** @@ -139,6 +128,19 @@ public static Map getAllPropertiesMap() { }); } + /** + * Returns all configuration properties, excluding properties with empty 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.isEmpty()) { + return MCRProperTree.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 +259,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 +502,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) { @@ -531,7 +525,7 @@ static void clearCaches() { // BEWARE: do NOT replace with constant value, 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(MCRProperTree.empty()); } private static class EventListener { 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..a1aed74afb 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.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 and. * 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