Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

/**
Expand Down Expand Up @@ -101,15 +97,10 @@ public class MCRConfiguration2 {

private static final Map<UUID, EventListener> LISTENERS = new ConcurrentHashMap<>();

private static final int CONFIGURATIONS_CACHE_SIZE = 32;

private static final Cache<ConfigurationKey, MCRInstanceConfiguration<?>> CONFIGURATIONS = CacheBuilder.newBuilder()
.maximumSize(CONFIGURATIONS_CACHE_SIZE)
.softValues()
.build();

private static final AtomicReference<Map<String, String>> PROPERTIES = new AtomicReference<>(Map.of());

private static final AtomicReference<MCRProperTree> PROPER_TREE = new AtomicReference<>(MCRPropertiesTree.EMPTY);

static Map<SingletonKey, Object> instanceHolder = new MCRConcurrentHashMap<>();

/**
Expand Down Expand Up @@ -139,6 +130,19 @@ public static Map<String, String> 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
Expand Down Expand Up @@ -257,16 +261,8 @@ private static <S> Optional<S> createSingleInstanceOf(MCRInstanceConfiguration<S
return Optional.ofNullable((S) instanceHolder.computeIfAbsent(key, _ -> configuration.instantiate()));
}

@SuppressWarnings({ "unchecked", "PMD.PreserveStackTrace" })
private static <S> MCRInstanceConfiguration<S> getConfiguration(Class<S> superClass, String name) {
try {
return ((MCRInstanceConfiguration<S>) 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);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the CONFIGURATIONS cache gone, every getInstanceOf / getSingleInstanceOf call runs resolveValueClass and thus MCRClassTools.forName again, even if the singleton is already in instanceHolder. For classes from plugin JARs in mycore-home/lib, Class.forName first throws a ClassNotFoundException on every call before the extendedClassLoader is tried. The cache was explicitly reintroduced in MCR-3668 (7d9a135). Is dropping it intended, or should the resolved configuration (or at least the value class) still be cached?

}

/**
Expand Down Expand Up @@ -508,8 +504,8 @@ public static boolean removePropertyChangeEventListener(UUID uuid) {
}

public static <S> S instantiateClass(Class<S> superClass, String className) {
return MCRInstanceConfiguration.ofClassName(superClass, className, "MCR.AnonymousInstance." + className)
.instantiate();
return MCRInstanceConfiguration.ofClassName(superClass, className, "MCR.AnonymousInstance." + className,
getAllPropertiesTree()).instantiate();
}

public static <S> Stream<S> instantiateClasses(Class<S> superClass, String propertyName) {
Expand All @@ -528,10 +524,10 @@ private static <T> Class<? extends T> 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 {
Expand Down Expand Up @@ -560,7 +556,4 @@ interface SingletonKey {
record ConfigSingletonKey(String property, String classname) implements SingletonKey {
}

record ConfigurationKey(String superclassName, String name) {
}

}
Original file line number Diff line number Diff line change
@@ -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<String> 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<String, String> toProperties() {
return Map.of();
}

@Override
public MCRTreeMessage toTreeMessage() {
return new MCRTreeMessage();
}

@Override
public String toString() {
return "{ }";
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.
* <p>
* 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
Expand Down Expand Up @@ -60,18 +58,17 @@ public final class MCRInstanceConfiguration<S> {

private final MCRInstanceName name;

private final Map<String, String> properties;
private final MCRProperTree properties;

private final Map<String, String> fullProperties;
private final MCRProperTree fullProperties;

private MCRInstanceConfiguration(Class<S> superClass, Class<? extends S> valueClass, MCRInstanceName name,
Map<String, String> properties, Map<String, String> 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() {
Expand All @@ -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 <S> MCRInstanceConfiguration<S> ofClassName(Class<S> superClass, String className,
String prefix) {
String prefix, MCRProperTree properties) {
try {
Class<? extends S> 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 <S> MCRInstanceConfiguration<S> ofClass(Class<S> superClass, Class<? extends S> 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.
* <p>
* Example: Given value class <code>Some.Instance.Name</code>,
* prefix <code>Some.Instance.Name</code> and
Expand All @@ -114,34 +120,55 @@ public static <S> MCRInstanceConfiguration<S> ofClassName(Class<S> superClass, S
* <li><code>Key1=Value1</code></li>
* <li><code>Key2=Value2</code></li>
* </ul>
* and {@link MCRInstanceConfiguration#fullProperties()} that are equal to the given properties.
*/
public static <S> MCRInstanceConfiguration<S> ofClass(Class<S> superClass, Class<? extends S> valueClass,
String prefix) {
String prefix, MCRProperTree properties) {
MCRInstanceName name = MCRInstanceName.of(prefix);
Map<String, String> properties = MCRConfiguration2.getAllPropertiesMap();
Map<String, String> 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 <T> MCRInstanceConfiguration<T> ofName(Class<T> 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 <T> MCRInstanceConfiguration<T> ofName(Class<T> 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 <T> MCRInstanceConfiguration<T> ofName(Class<T> superClass, String name, Set<Option> options) {
return ofName(superClass, MCRInstanceName.of(name), options);
return ofName(superClass, MCRInstanceName.of(name), MCRConfiguration2.getAllPropertiesTree(), options);
}

/**
* Shorthand for {@link MCRInstanceConfiguration#ofName(Class, MCRInstanceName, MCRProperTree, Set)} that
* creates the name with {@link MCRInstanceName#of(String)}.
*/
public static <T> MCRInstanceConfiguration<T> ofName(Class<T> superClass, String name,
MCRProperTree properties, Set<Option> options) {
return ofName(superClass, MCRInstanceName.of(name), properties, options);
}

/**
* Creates a new configuration for the given super class and instance name.
* Creates a new configuration for the given super class and instance name based on the given properties.
* <p>
* Example: Given an {@link MCRInstanceName} <code>Some.Instance.Name</code> and
* properties
Expand All @@ -161,17 +188,16 @@ public static <T> MCRInstanceConfiguration<T> ofName(Class<T> superClass, String
* and {@link MCRInstanceConfiguration#fullProperties()} that are equal to the given properties.
*/
public static <T> MCRInstanceConfiguration<T> ofName(Class<T> superClass, MCRInstanceName name,
Set<Option> options) {
Map<String, String> properties = MCRConfiguration2.getAllPropertiesMap();
Map<String, String> reducedProperties = reduceProperties(name.canonical(), properties);
Class<? extends T> valueClass = resolveValueClass(superClass, name, reducedProperties, options);
return new MCRInstanceConfiguration<>(superClass, valueClass, name, reducedProperties, properties);
MCRProperTree properties, Set<Option> options) {
MCRProperTree nestedProperties = properties.deeplyNested(name.canonical());
Class<? extends T> valueClass = resolveValueClass(superClass, name, nestedProperties, options);
return new MCRInstanceConfiguration<>(superClass, valueClass, name, nestedProperties, properties);
}

private static <S> Class<? extends S> resolveValueClass(Class<S> superClass, MCRInstanceName name,
Map<String, String> properties, Set<Option> options) {
MCRProperTree properties, Set<Option> options) {

String className = properties.get("Class");
String className = properties.nested(CLASS_KEY).value();
if (className != null) {
try {
return MCRClassTools.forName(className);
Expand All @@ -191,27 +217,6 @@ private static <S> Class<? extends S> resolveValueClass(Class<S> superClass, MCR

}

private static Map<String, String> reduceProperties(String prefix, Map<String, String> properties) {

final String prefixWithDelimiter = prefix + '.';
final int prefixWithDelimiterLength = prefixWithDelimiter.length();

Map<String, String> reducedProperties = new HashMap<>();
for (Map.Entry<String, String> entry : properties.entrySet()) {
String key = entry.getKey();
if (!key.startsWith(prefixWithDelimiter)) {
continue;
}
String reducedKey = key.substring(prefixWithDelimiterLength);
reducedProperties.put(reducedKey, entry.getValue());
}
String directProperty = properties.get(prefix);
if (directProperty != null) {
reducedProperties.put("", directProperty);
}
return reducedProperties;
}

public Class<S> superClass() {
return superClass;
}
Expand All @@ -224,16 +229,16 @@ public MCRInstanceName name() {
return name;
}

public Map<String, String> properties() {
public MCRProperTree properties() {
return properties;
}

public Map<String, String> fullProperties() {
public MCRProperTree fullProperties() {
return fullProperties;
}

public MCRInstanceConfiguration<S> copy() {
return new MCRInstanceConfiguration<>(superClass, valueClass, name, new HashMap<>(properties), fullProperties);
return new MCRInstanceConfiguration<>(superClass, valueClass, name, properties, fullProperties);
}

@Override
Expand All @@ -242,12 +247,11 @@ public String toString() {
"superClass=" + superClass.getName() + ", " +
"valueClass=" + (valueClass == null ? "null" : valueClass.getName()) + ", " +
"name=" + name + ", " +
"properties=" + properties + ", " +
"#fullProperties=" + fullProperties.size() + "}";
"properties=" + properties.toProperties() + "}";
}

public static <N> MCRInstanceConfiguration<N> ofComponents(Class<N> superClass, MCRInstanceName name,
Map<String, String> properties, Map<String, String> fullProperties) {
MCRProperTree properties, MCRProperTree fullProperties) {
Class<? extends N> valueClass = resolveValueClass(superClass, name, properties, Options.IMPLICIT);
return new MCRInstanceConfiguration<>(superClass, valueClass, name, properties, fullProperties);
}
Expand Down
Loading