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 f13d836903..2ba801f3f6 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 @@ -25,6 +25,7 @@ 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; import java.util.function.Supplier; @@ -107,12 +108,41 @@ public class MCRConfiguration2 { .softValues() .build(); + private static final AtomicReference> PROPERTIES = new AtomicReference<>(null); + static Map instanceHolder = new MCRConcurrentHashMap<>(); + /** + * @deprecated Use {@link #getRawProperties()} instead. + */ + @Deprecated public static Map getPropertiesMap() { + return getRawProperties(); + } + + /** + * Returns all configuration properties, including properties with empty values, i.e. + * properties that are not recognized by, for example, {@link #getString(String)}. + */ + public static Map getRawProperties() { return Collections.unmodifiableMap(MCRConfigurationBase.getResolvedProperties().getAsMap()); } + /** + * Returns all configuration properties, excluding properties with empty values, i.e. + * only properties that are recognized by, for example, {@link #getString(String)}. + */ + public static Map getAllProperties() { + return PROPERTIES.updateAndGet(existingValue -> { + if (existingValue != null) { + return existingValue; + } + return getRawProperties().entrySet().stream() + .filter(entry -> !entry.getValue().isBlank()) + .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().trim())); + }); + } + /** * Returns a sub map of properties where key is transformed. * @@ -135,7 +165,7 @@ public static Map getPropertiesMap() { * @return a map of the properties as stated above */ public static Map getSubPropertiesMap(String propertyPrefix) { - return getPropertiesMap() + return getRawProperties() .entrySet() .stream() .filter(e -> e.getKey().startsWith(propertyPrefix)) @@ -226,7 +256,7 @@ private static MCRInstanceConfiguration getConfiguration(Class superCl try { return ((MCRInstanceConfiguration) CONFIGURATIONS.get( new ConfigurationKey(superClass.getName(), name), - () -> MCRInstanceConfiguration.ofName(superClass, name, Options.IMPLICIT))).copy(); + () -> MCRInstanceConfiguration.ofName(superClass, name, Options.IMPLICIT))); } catch (ExecutionException | UncheckedExecutionException e) { Throwables.throwIfUnchecked(e.getCause()); throw new MCRException("Failed to create instance configuration for " + name, e.getCause()); @@ -494,6 +524,7 @@ private static Class getClassObject(String classname) { } static void clearCaches() { + PROPERTIES.set(null); CONFIGURATIONS.invalidateAll(); } diff --git a/mycore-base/src/main/java/org/mycore/common/config/MCRPropertiesURIResolver.java b/mycore-base/src/main/java/org/mycore/common/config/MCRPropertiesURIResolver.java index 4b81db3a72..25178aa764 100644 --- a/mycore-base/src/main/java/org/mycore/common/config/MCRPropertiesURIResolver.java +++ b/mycore-base/src/main/java/org/mycore/common/config/MCRPropertiesURIResolver.java @@ -105,7 +105,7 @@ private JDOMSource resolveKey(String key) { final Element entryElement = new Element("entry"); entryElement.setAttribute("key", key); - String value = MCRConfiguration2.getPropertiesMap().get(key); + String value = MCRConfiguration2.getRawProperties().get(key); if (value != null) { entryElement.setText(value); } diff --git a/mycore-base/src/main/java/org/mycore/common/config/annotation/MCRClassProperty.java b/mycore-base/src/main/java/org/mycore/common/config/annotation/MCRClassProperty.java new file mode 100644 index 0000000000..e454af1499 --- /dev/null +++ b/mycore-base/src/main/java/org/mycore/common/config/annotation/MCRClassProperty.java @@ -0,0 +1,75 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.common.config.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.mycore.common.config.MCRConfigurationException; + +/** + * This annotation is used to mark fields or methods that should be set to or called with a value of type + * {@link Class} from the configuration properties. + *

+ * The field or method needs to be public. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.METHOD, ElementType.FIELD }) +@Inherited +public @interface MCRClassProperty { + + /** + * @return The name of property containing the string value. + */ + String name(); + + /** + * @return The class or a superclass of the configured class. + */ + Class valueClass() default Object.class; + + /** + * @return true if the property specified by {@link MCRClassProperty#name()} has to be present in the properties. + * {@link MCRConfigurationException} is thrown if the property is required but not present. + */ + boolean required() default true; + + /** + * @return true if the property name specified by {@link MCRClassProperty#name()} is absolute and not specific for + * this instance e.g. MCR.Foo.Bar. + */ + boolean absolute() default false; + + /** + * @return The name for a default property that should be used as a fallback, if no value is configured. + * A {@link MCRConfigurationException} is thrown if the default property is not configured. + * The default property must be absolute, e.g. MCR.Foo.Bar. + */ + String defaultName() default ""; + + /** + * @return The order in which the annotated fields or methods are processed. The higher the value, the later the + * field or method is processed. All fields are processed first, then all methods are processed. + */ + int order() default 0; + +} diff --git a/mycore-base/src/main/java/org/mycore/common/config/annotation/MCRClassPropertyList.java b/mycore-base/src/main/java/org/mycore/common/config/annotation/MCRClassPropertyList.java new file mode 100644 index 0000000000..ae070ba2c0 --- /dev/null +++ b/mycore-base/src/main/java/org/mycore/common/config/annotation/MCRClassPropertyList.java @@ -0,0 +1,76 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.common.config.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.mycore.common.config.MCRConfigurationException; + +/** + * This annotation is used to mark fields or methods that should be set to or called with + * a list of {@link Class} values from the configuration properties. + *

+ * The field or method needs to be public. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.METHOD, ElementType.FIELD }) +@Inherited +public @interface MCRClassPropertyList { + + /** + * @return The prefix for names of properties containing the list values. + */ + String name() default ""; + + /** + * @return The class or a superclass of the configured class. + */ + Class valueClass() default Object.class; + + /** + * @return true if the at least one sub-property of the property specified by {@link MCRClassPropertyList#name()} + * has to be present in the properties. {@link MCRConfigurationException} is thrown if a value is required + * but not present. + */ + boolean required() default true; + + /** + * @return true if the property name specified by {@link MCRClassPropertyList#name()} is absolute and not specific + * for this instance e.g. MCR.Foo.Bar. + */ + boolean absolute() default false; + + /** + * @return The name for a default property that should be used as a fallback, if no list values are configured. + * A {@link MCRConfigurationException} is thrown if no default property is not configured. + * The default property must be absolute, e.g. MCR.Foo.Bar. + */ + String defaultName() default ""; + + /** + * @return The order in which the annotated fields or methods are processed. The higher the value, the later the + * field or method is processed. All fields are processed first, then all methods are processed. + */ + int order() default 0; + +} diff --git a/mycore-base/src/main/java/org/mycore/common/config/annotation/MCRClassPropertyMap.java b/mycore-base/src/main/java/org/mycore/common/config/annotation/MCRClassPropertyMap.java new file mode 100644 index 0000000000..5524f3f5d2 --- /dev/null +++ b/mycore-base/src/main/java/org/mycore/common/config/annotation/MCRClassPropertyMap.java @@ -0,0 +1,76 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.common.config.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.mycore.common.config.MCRConfigurationException; + +/** + * This annotation is used to mark fields or methods that should be set to or called with + * a map of {@link Class} values from the configuration properties. + *

+ * The field or method needs to be public. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.METHOD, ElementType.FIELD }) +@Inherited +public @interface MCRClassPropertyMap { + + /** + * @return The prefix for names of properties containing the map values. + */ + String name() default ""; + + /** + * @return The class or a superclass of the configured class. + */ + Class valueClass() default Object.class; + + /** + * @return true if the at least one sub-property of the property specified by {@link MCRClassPropertyMap#name()} + * has to be present in the properties. {@link MCRConfigurationException} is thrown if a value is required + * but not present. + */ + boolean required() default true; + + /** + * @return true if the property name specified by {@link MCRClassPropertyMap#name()} is absolute and not specific + * for this instance e.g. MCR.Foo.Bar. + */ + boolean absolute() default false; + + /** + * @return The name for a default property that should be used as a fallback, if no map values are configured. + * A {@link MCRConfigurationException} is thrown if no default property is not configured. + * The default property must be absolute, e.g. MCR.Foo.Bar. + */ + String defaultName() default ""; + + /** + * @return The order in which the annotated fields or methods are processed. The higher the value, the later the + * field or method is processed. All fields are processed first, then all methods are processed. + */ + int order() default 0; + +} 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 a397e7c1ca..2d81f0fe58 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,6 +19,7 @@ package org.mycore.common.config.instantiator; import java.lang.reflect.Modifier; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -28,10 +29,9 @@ import org.mycore.common.MCRClassTools; import org.mycore.common.MCRException; import org.mycore.common.config.MCRConfiguration2; -import org.mycore.common.config.MCRConfigurationException; /** - * Represents an extract of properties (typically {@link MCRConfiguration2#getPropertiesMap()}) used to + * Represents an extract of properties (typically {@link MCRConfiguration2#getAllProperties()}) used to * instantiate an object. Provides methods to extract nested configurations. *

* Generally speaking, a configuration has a {@link MCRInstanceConfiguration#name()} that represents the @@ -66,12 +66,12 @@ public final class MCRInstanceConfiguration { private MCRInstanceConfiguration(Class superClass, Class valueClass, MCRInstanceName name, Map properties, Map fullProperties) { + properties.remove(CLASS_KEY); this.superClass = superClass; this.valueClass = valueClass; this.name = name; - this.properties = properties; + this.properties = Collections.unmodifiableMap(properties); this.fullProperties = fullProperties; - properties.remove(CLASS_KEY); } public boolean instantiatable() { @@ -85,11 +85,11 @@ public S instantiate() { /** * Shorthand for {@link MCRInstanceConfiguration#ofClass(Class, Class, String, Map)} that * uses {@link MCRClassTools#forName(String)} to resolve the value class and - * uses {@link MCRConfiguration2#getPropertiesMap()} as the properties. + * uses {@link MCRConfiguration2#getAllProperties()} as the properties. */ public static MCRInstanceConfiguration ofClassName(Class superClass, String className, String prefix) { - return ofClassName(superClass, className, prefix, MCRConfiguration2.getPropertiesMap()); + return ofClassName(superClass, className, prefix, MCRConfiguration2.getAllProperties()); } /** @@ -108,11 +108,11 @@ public static MCRInstanceConfiguration ofClassName(Class superClass, S /** * Shorthand for {@link MCRInstanceConfiguration#ofClass(Class, Class, String, Map)} that - * uses {@link MCRConfiguration2#getPropertiesMap()} as the properties. + * uses {@link MCRConfiguration2#getAllProperties()} as the properties. */ public static MCRInstanceConfiguration ofClass(Class superClass, Class valueClass, String prefix) { - return ofClass(superClass, valueClass, prefix, MCRConfiguration2.getPropertiesMap()); + return ofClass(superClass, valueClass, prefix, MCRConfiguration2.getAllProperties()); } /** @@ -143,11 +143,11 @@ public static MCRInstanceConfiguration ofClass(Class superClass, Class /** * Shorthand for {@link MCRInstanceConfiguration#ofName(Class, MCRInstanceName, Map, Set)} that * creates the name with {@link MCRInstanceName#of(String)} and - * uses {@link MCRConfiguration2#getPropertiesMap()} as the properties and + * uses {@link MCRConfiguration2#getAllProperties()} as the properties and * uses {@link Options#NONE} as the options. */ public static MCRInstanceConfiguration ofName(Class superClass, String name) { - return ofName(superClass, MCRInstanceName.of(name), MCRConfiguration2.getPropertiesMap(), Options.NONE); + return ofName(superClass, MCRInstanceName.of(name), MCRConfiguration2.getAllProperties(), Options.NONE); } /** @@ -163,11 +163,11 @@ public static MCRInstanceConfiguration ofName(Class superClass, String /** * Shorthand for {@link MCRInstanceConfiguration#ofName(Class, MCRInstanceName, Map, Set)} that * creates the name with {@link MCRInstanceName#of(String)} and - * uses {@link MCRConfiguration2#getPropertiesMap()} as the properties. + * uses {@link MCRConfiguration2#getAllProperties()} as the properties. */ public static MCRInstanceConfiguration ofName(Class superClass, String name, Set

- * Example: Given an {@link MCRInstanceConfiguration} - * representing the {@link MCRInstanceName} Some.Instance.Name, properties - *

    - *
  • Foo.Class=some.nested.ClassName
  • - *
  • Foo.Key1=Value1
  • - *
  • Foo.Key2=Value2
  • - *
  • Bar=UnrelatedValue
  • - *
- * and a prefix of Foo, this will return a an {@link MCRInstanceConfiguration} - * representing the {@link MCRInstanceConfiguration#name()} Some.Instance.Name.Foo, - * {@link MCRInstanceConfiguration#valueClass()} some.nested.ClassName - * and properties - *
    - *
  • Key1=Value1
  • - *
  • Key2=Value2
  • - *
- * - * @param prefix the prefix - * @return the nested configuration - */ - public MCRInstanceConfiguration nested(Class superClass, String prefix) { - MCRInstanceName nestedName = name.nested(prefix); - Map reducedProperties = reduceProperties(prefix, properties); - Class valueClass = resolveValueClass(superClass, nestedName, reducedProperties, Options.IMPLICIT); - return new MCRInstanceConfiguration<>(superClass, valueClass, nestedName, reducedProperties, fullProperties); - } - - /** - * Returns a {@link Map} of configurations for nested instances, mapped by the first name segment. - *

- * Example: Given an {@link MCRInstanceConfiguration} - * representing the {@link MCRInstanceName} Some.Instance.Name, properties - *

    - *
  • A.Class=some.nested.ClassNameA
  • - *
  • A.Key1=ValueA1
  • - *
  • A.Key2=ValueA2
  • - *
  • B.Class=some.nested.ClassNameB
  • - *
  • B.Key1=ValueB1
  • - *
  • B.Key2=ValueB2
  • - *
- * this will return a map containing - *
    - *
  1. - * an entry with key A mapping to an {@link MCRInstanceConfiguration} - * representing the {@link MCRInstanceConfiguration#name()} Some.Instance.Name.A, - * {@link MCRInstanceConfiguration#valueClass()} some.nested.ClassNameA - * and {@link MCRInstanceConfiguration#properties()} - *
      - *
    • Key1=ValueA1
    • - *
    • Key2=ValueA2
    • - *
    - * and {@link MCRInstanceConfiguration#fullProperties()} that are equal to the full properties of this - * configuration (i.e. the full properties used to create the top level configuration). - *
  2. - *
  3. - * an entry with key B mapping to an {@link MCRInstanceConfiguration} - * representing the {@link MCRInstanceConfiguration#name()} Some.Instance.Name.B, - * {@link MCRInstanceConfiguration#valueClass()} some.nested.ClassNameB - * and {@link MCRInstanceConfiguration#properties()} - *
      - *
    • Key1=ValueB1
    • - *
    • Key2=ValueB2
    • - *
    - * and {@link MCRInstanceConfiguration#fullProperties()} that are equal to the full properties of this - * configuration (i.e. the full properties used to create the top level configuration). - *
  4. - *
- *

- * - * @return the nested configuration map - */ - public Map> nestedMap(Class superClass) { - Map> nestedConfigurationMap = new HashMap<>(); - for (Map.Entry entry : properties().entrySet()) { - String key = entry.getKey(); - int index = key.indexOf('.'); - String nestedConfigurationKey = -1 == index ? key : key.substring(0, index); - if (!nestedConfigurationMap.containsKey(nestedConfigurationKey)) { - String nestedConfigurationSuffix = nestedConfigurationKey; - nestedConfigurationMap.put(nestedConfigurationKey, - nested(superClass, nestedConfigurationSuffix)); - } - } - return nestedConfigurationMap; - } - - /** - * Returns a {@link Map} of configurations for nested instances with a common prefix, mapped by the - * name segment following that common prefix. - *

- * Example: Given an {@link MCRInstanceConfiguration} - * representing the {@link MCRInstanceName} Some.Instance.Name, properties - *

    - *
  • Foo.A.Class=come.nested.ClassNameA
  • - *
  • Foo.A.Key1=ValueA1
  • - *
  • Foo.A.Key2=ValueA2
  • - *
  • Foo.B.Class=some.nested.ClassNameB
  • - *
  • Foo.B.Key1=ValueB1
  • - *
  • Foo.B.Key2=ValueB2
  • - *
  • Bar=UnrelatedValue
  • - *
- * and a commonPrefix of Foo, this will return a map containing - *
    - *
  1. - * an entry with key A mapping to an {@link MCRInstanceConfiguration} - * representing the {@link MCRInstanceConfiguration#name()} Some.Instance.Name.Foo.A, - * {@link MCRInstanceConfiguration#valueClass()} some.nested.ClassNameA - * and {@link MCRInstanceConfiguration#properties()} - *
      - *
    • Key1=ValueA1
    • - *
    • Key2=ValueA2
    • - *
    - * and {@link MCRInstanceConfiguration#fullProperties()} that are equal to the full properties of this - * configuration (i.e. the full properties used to create the top level configuration). - *
  2. - *
  3. - * an entry with key B mapping to an {@link MCRInstanceConfiguration} - * representing the {@link MCRInstanceConfiguration#name()} Some.Instance.Name.Foo.B, - * {@link MCRInstanceConfiguration#valueClass()} some.nested.ClassNameB - * and {@link MCRInstanceConfiguration#properties()} - *
      - *
    • Key1=ValueB1
    • - *
    • Key2=ValueB2
    • - *
    - * and {@link MCRInstanceConfiguration#fullProperties()} that are equal to the full properties of this - * configuration (i.e. the full properties used to create the top level configuration). - *
  4. - *
- * - * @param prefix the common prefix - * @return the nested configuration map - */ - public Map> nestedMap(Class superClass, String prefix) { - if (prefix.isEmpty()) { - return nestedMap(superClass); - } - String suffixWithDelimiter = prefix + "."; - Map> nestedConfigurationMap = new HashMap<>(); - for (Map.Entry entry : properties().entrySet()) { - String key = entry.getKey(); - if (key.startsWith(suffixWithDelimiter)) { - String remainingKey = key.substring(suffixWithDelimiter.length()); - int index = remainingKey.indexOf('.'); - String nestedConfigurationKey = -1 == index ? remainingKey : remainingKey.substring(0, index); - if (!nestedConfigurationMap.containsKey(nestedConfigurationKey)) { - String nestedConfigurationSuffix = suffixWithDelimiter + nestedConfigurationKey; - nestedConfigurationMap.put(nestedConfigurationKey, - nested(superClass, nestedConfigurationSuffix)); - } - } - } - return nestedConfigurationMap; - } - public MCRInstanceConfiguration copy() { return new MCRInstanceConfiguration<>(superClass, valueClass, name, new HashMap<>(properties), fullProperties); } @@ -443,6 +281,12 @@ public String toString() { "#fullProperties=" + fullProperties.size() + "}"; } + public static MCRInstanceConfiguration ofComponents(Class superClass, MCRInstanceName name, + Map properties, Map fullProperties) { + Class valueClass = resolveValueClass(superClass, name, properties, Options.IMPLICIT); + return new MCRInstanceConfiguration<>(superClass, valueClass, name, properties, fullProperties); + } + public enum Option { /** diff --git a/mycore-base/src/main/java/org/mycore/common/config/instantiator/MCRInstantiatorUtils.java b/mycore-base/src/main/java/org/mycore/common/config/instantiator/MCRInstantiatorUtils.java index 2ee57e1863..c82bb56a31 100644 --- a/mycore-base/src/main/java/org/mycore/common/config/instantiator/MCRInstantiatorUtils.java +++ b/mycore-base/src/main/java/org/mycore/common/config/instantiator/MCRInstantiatorUtils.java @@ -19,20 +19,12 @@ package org.mycore.common.config.instantiator; import java.lang.reflect.Method; -import java.util.ArrayList; import java.util.List; import java.util.Locale; -import java.util.Map; -import java.util.Objects; -import java.util.SortedMap; -import java.util.TreeMap; import java.util.stream.Collectors; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.mycore.common.MCRClassTools; import org.mycore.common.config.MCRConfigurationException; -import org.mycore.common.config.annotation.MCRSentinel; import org.mycore.common.config.instantiator.source.MCRSource; import org.mycore.common.config.instantiator.target.MCRTarget; @@ -42,8 +34,6 @@ @SuppressWarnings("PMD.AvoidDuplicateLiterals") public final class MCRInstantiatorUtils { - private static final Logger LOGGER = LogManager.getLogger(); - private MCRInstantiatorUtils() { } @@ -56,43 +46,6 @@ public static Class getClass(String property, String className) { } } - public static Object createInstance(MCRTarget target, MCRInstanceConfiguration configuration, - MCRSentinel sentinel, String description) { - - String property = configuration.name().canonical(); - - if (sentinel != null) { - boolean sentinelValue = sentinel.defaultValue(); - String configuredSentinelValue = configuration.properties().remove(sentinel.name()); - if (configuredSentinelValue != null) { - sentinelValue = Boolean.parseBoolean(configuredSentinelValue); - } - if (sentinelValue == sentinel.rejectionValue()) { - if (LOGGER.isInfoEnabled()) { - LOGGER.info("[SENTINEL] Ignoring {} {} and all sub-properties", description, property); - } - return null; - } - } - - if (!configuration.instantiatable()) { - if (LOGGER.isInfoEnabled()) { - LOGGER.info("[CLEAN-UP] Ignoring {} {} and all sub-properties (no or empty class name)", - description, property); - } - return null; - } - - Object instance = configuration.instantiate(); - - if (!configuration.valueClass().isAssignableFrom(instance.getClass())) { - throw incompatibilityException(property, target, configuration.valueClass(), instance); - } - - return instance; - - } - public static String methodNames(List methods) { return methods.stream() .map(Method::getName) @@ -113,14 +66,6 @@ public static String annotationClassName(MCRSource source) { return source.annotationClass().getName(); } - public static String property(MCRInstanceConfiguration configuration, String annotationName) { - if (Objects.equals("", annotationName)) { - return configuration.name().canonical(); - } else { - return configuration.name().canonical() + "." + annotationName; - } - } - public static MCRConfigurationException emptyNameException(MCRTarget target) { return new MCRConfigurationException("The name for target " + targetTypeName(target) + " '" + target.name() + "' in configured class " + target.declaringClass().getName() + " must not be empty"); @@ -128,17 +73,9 @@ public static MCRConfigurationException emptyNameException(MCRTarget target) { public static MCRConfigurationException incompatibilityException(String property, Class instanceClass, Class superClass) { - return new MCRConfigurationException("Instance of class " + instanceClass.getName() - + ", configured in " + property + ", is incompatible with" + - " intended super class " + superClass.getName()); - } - - public static MCRConfigurationException incompatibilityException(String property, MCRTarget target, - Class annotationValueClass, Object instance) { - return new MCRConfigurationException("Instance of class " + instance.getClass().getName() - + "', configured in " + property + ", is incompatible with annotated value class " - + annotationValueClass.getName() + " for target " + targetTypeName(target) - + " '" + target.name() + "' in configured class " + target.declaringClass().getName()); + return new MCRConfigurationException("Instance, configured in " + property + ", has a class (" + + instanceClass.getName() + ") that is incompatible with the intended super class (" + + superClass.getName() + ")"); } public static MCRConfigurationException missingException(String property, Class superClass) { @@ -147,73 +84,16 @@ public static MCRConfigurationException missingException(String property, Class< + " and therefore cannot be used implicitly)"); } - public static MCRConfigurationException missingException(String property, MCRTarget target, - String description) { - return new MCRConfigurationException( - capitalize(description) + ", configured in " + property + " (and its sub-properties)," + - " for target " + targetTypeName(target) + " '" + target.name() + "' in configured class " - + target.declaringClass().getName() - + " is missing"); - } - - public static MCRConfigurationException emptyException(String property, MCRTarget target, String description) { - return new MCRConfigurationException( - capitalize(description) + ", configured in " + property + " (and its sub-properties)," + - " for target " + targetTypeName(target) + " '" + target.name() + "' in configured class " - + target.declaringClass().getName() - + " is empty"); - } - public static MCRConfigurationException emptyRawException(String property, MCRTarget target, String description) { - return new MCRConfigurationException( - capitalize(description) + ", configured in " + property + "," + - " for target " + targetTypeName(target) + " '" + target.name() + "' in configured class " - + target.declaringClass().getName() - + " is empty"); - } - - public static MCRConfigurationException nonIntegerKeyException(String property, MCRTarget target, - String key, String description, NumberFormatException exception) { - return new MCRConfigurationException( - capitalize(description) + ", configured in " + property + " (and its sub-properties)," + - " for target " + targetTypeName(target) + " '" + target.name() + "' in configured class " - + target.declaringClass().getName() + " has element with non-integer key " + key, - exception); + return new MCRConfigurationException(capitalize(description) + ", configured in " + property + "," + + " for target " + targetTypeName(target) + " '" + target.name() + "' in configured class " + + target.declaringClass().getName() + + " is empty"); } public static String capitalize(String description) { return description.substring(0, 1).toUpperCase(Locale.ROOT) + description.substring(1); } - public static MCRConfigurationException inconsistentKeysException(String property, MCRTarget target, - String key1, String key2, String description) { - return new MCRConfigurationException( - capitalize(description) + ", configured in " + property + " (and its sub-properties)," - + " for target " + targetTypeName(target) + " '" + target.name() + "' in configured class " - + target.declaringClass().getName() + " has element with inconsistent integer keys " - + key1 + " and " + key2); - } - - @SuppressWarnings("PMD.PreserveStackTrace") - public static List orderedKeys(String property, MCRTarget target, Map map, - String description) { - - SortedMap keyMap = new TreeMap<>(); - for (String key : map.keySet()) { - try { - Integer integerValue = Integer.parseInt(key); - String alreadyMappedKey = keyMap.put(integerValue, key); - if (alreadyMappedKey != null && !alreadyMappedKey.equals(key)) { - throw inconsistentKeysException(property, target, key, alreadyMappedKey, description); - } - } catch (NumberFormatException e) { - throw nonIntegerKeyException(property, target, key, description, e); - } - } - - return new ArrayList<>(keyMap.values()); - - } - } diff --git a/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRClassPropertyExtractor.java b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRClassPropertyExtractor.java new file mode 100644 index 0000000000..9b68a7498e --- /dev/null +++ b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRClassPropertyExtractor.java @@ -0,0 +1,59 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.common.config.instantiator.source; + +import java.util.Map; + +import org.mycore.common.MCRClassTools; + +/** + * A {@link MCRClassPropertyExtractor} is a {@link MCRPropertyExtractor} that uses + * {@link MCRClassTools#forName(String)} to extract {@link Class} values. + */ +final class MCRClassPropertyExtractor implements MCRValueExtractor> { + + private final Class superClass; + + MCRClassPropertyExtractor(Class valueClass) { + this.superClass = valueClass; + } + + @Override + public Class toValue(MCRSourceContext context, Map properties, + Map fullProperties) { + + String className = properties.get(""); + + if (className == null) { + return null; + } + + try { + Class configuredClass = MCRClassTools.forName(className); + if (!superClass.isAssignableFrom(configuredClass)) { + throw context.classIncompatibilityException(superClass, configuredClass); + } + return configuredClass; + } catch (ClassNotFoundException exception) { + throw context.classLoadingException(className, exception); + } + + } + +} diff --git a/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRClassPropertyListSource.java b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRClassPropertyListSource.java new file mode 100644 index 0000000000..669929d2b4 --- /dev/null +++ b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRClassPropertyListSource.java @@ -0,0 +1,99 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.common.config.instantiator.source; + +import java.util.List; +import java.util.Set; + +import org.mycore.common.config.annotation.MCRClassPropertyList; +import org.mycore.common.config.instantiator.target.MCRTarget; + +/** + * A {@link MCRClassPropertyListSource} is a {@link MCRSource} that interprets a {@link MCRClassPropertyList}. + */ +final class MCRClassPropertyListSource extends MCRValueListSourceBase> { + + private final MCRClassPropertyList annotation; + + MCRClassPropertyListSource(MCRClassPropertyList annotation, MCRAnnotationProvider annotationProvider) { + super(annotationProvider, new MCRClassPropertyExtractor(annotation.valueClass())); + this.annotation = annotation; + } + + @Override + public Type type() { + return Type.PROPERTY_LIST; + } + + @Override + public Class annotationClass() { + return MCRClassPropertyList.class; + } + + @Override + public int order() { + return annotation.order(); + } + + @Override + public Set allowedTargetTypes() { + return MCRTarget.Types.ALL; + } + + @Override + public Class valueClass() { + return List.class; + } + + @Override + protected String description() { + return "class list"; + } + + @Override + protected String name() { + return annotation.name(); + } + + @Override + protected String defaultName() { + return annotation.defaultName(); + } + + @Override + protected boolean supportsEmptyName() { + return true; + } + + @Override + protected boolean absoluteName() { + return annotation.absolute(); + } + + @Override + protected boolean supportsShortForm() { + return true; + } + + @Override + protected boolean required() { + return annotation.required(); + } + +} diff --git a/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRClassPropertyMapSource.java b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRClassPropertyMapSource.java new file mode 100644 index 0000000000..152aa49b64 --- /dev/null +++ b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRClassPropertyMapSource.java @@ -0,0 +1,99 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.common.config.instantiator.source; + +import java.util.Map; +import java.util.Set; + +import org.mycore.common.config.annotation.MCRClassPropertyMap; +import org.mycore.common.config.instantiator.target.MCRTarget; + +/** + * A {@link MCRClassPropertyMapSource} is a {@link MCRSource} that interprets a {@link MCRClassPropertyMap}. + */ +final class MCRClassPropertyMapSource extends MCRValueMapSourceBase> { + + private final MCRClassPropertyMap annotation; + + MCRClassPropertyMapSource(MCRClassPropertyMap annotation, MCRAnnotationProvider annotationProvider) { + super(annotationProvider, new MCRClassPropertyExtractor(annotation.valueClass())); + this.annotation = annotation; + } + + @Override + public Type type() { + return Type.PROPERTY_MAP; + } + + @Override + public Class annotationClass() { + return MCRClassPropertyMap.class; + } + + @Override + public int order() { + return annotation.order(); + } + + @Override + public Set allowedTargetTypes() { + return MCRTarget.Types.ALL; + } + + @Override + public Class valueClass() { + return Map.class; + } + + @Override + protected String description() { + return "class map"; + } + + @Override + protected String name() { + return annotation.name(); + } + + @Override + protected String defaultName() { + return annotation.defaultName(); + } + + @Override + protected boolean supportsEmptyName() { + return true; + } + + @Override + protected boolean absoluteName() { + return annotation.absolute(); + } + + @Override + protected boolean supportsShortForm() { + return true; + } + + @Override + protected boolean required() { + return annotation.required(); + } + +} diff --git a/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRClassPropertySource.java b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRClassPropertySource.java new file mode 100644 index 0000000000..669e18ab72 --- /dev/null +++ b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRClassPropertySource.java @@ -0,0 +1,93 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.common.config.instantiator.source; + +import java.util.Set; + +import org.mycore.common.config.annotation.MCRClassProperty; +import org.mycore.common.config.instantiator.target.MCRTarget; + +/** + * A {@link MCRClassPropertySource} is a {@link MCRSource} that interprets a {@link MCRClassProperty}. + */ +final class MCRClassPropertySource extends MCRValueSourceBase> { + + private final MCRClassProperty annotation; + + MCRClassPropertySource(MCRClassProperty annotation, MCRAnnotationProvider annotationProvider) { + super(annotationProvider, new MCRClassPropertyExtractor(annotation.valueClass())); + this.annotation = annotation; + } + + @Override + public Type type() { + return Type.PROPERTY; + } + + @Override + public Class annotationClass() { + return MCRClassProperty.class; + } + + @Override + public int order() { + return annotation.order(); + } + + @Override + public Set allowedTargetTypes() { + return MCRTarget.Types.ALL; + } + + @Override + public Class valueClass() { + return Class.class; + } + + @Override + protected String description() { + return "class"; + } + + @Override + protected String name() { + return annotation.name(); + } + + @Override + protected String defaultName() { + return annotation.defaultName(); + } + + @Override + protected boolean supportsEmptyName() { + return false; + } + + @Override + protected boolean absoluteName() { + return annotation.absolute(); + } + + @Override + protected boolean required() { + return annotation.required(); + } + +} diff --git a/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRInstanceExtractor.java b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRInstanceExtractor.java new file mode 100644 index 0000000000..f2b4ba059e --- /dev/null +++ b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRInstanceExtractor.java @@ -0,0 +1,66 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.common.config.instantiator.source; + +import java.util.Map; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.mycore.common.config.instantiator.MCRInstanceConfiguration; +import org.mycore.common.config.instantiator.MCRInstanceName; + +/** + * A {@link MCRClassPropertyExtractor} is a {@link MCRPropertyExtractor} that uses + * {@link MCRInstanceConfiguration#instantiate()} to extract nested configured instances. + */ +final class MCRInstanceExtractor implements MCRValueExtractor { + + private static final Logger LOGGER = LogManager.getLogger(); + + private final Class superClass; + + MCRInstanceExtractor(Class superClass) { + this.superClass = superClass; + } + + @Override + public Object toValue(MCRSourceContext context, Map properties, + Map fullProperties) { + + MCRInstanceConfiguration configuration = MCRInstanceConfiguration.ofComponents( + superClass, MCRInstanceName.of(context.property()), properties, fullProperties); + + if (!configuration.instantiatable()) { + if (LOGGER.isInfoEnabled()) { + LOGGER.info("[CLEAN-UP] Ignoring {}, configured in {} (and sub-properties thereof), " + + "because {}.Class is missing or empty)", context.description(), context.property(), + context.property()); + } + return null; + } + + if (!superClass.isAssignableFrom(configuration.valueClass())) { + throw context.classIncompatibilityException(superClass, configuration.valueClass()); + } + + return configuration.instantiate(); + + } + +} diff --git a/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRInstanceListSource.java b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRInstanceListSource.java index 5dd4e6b7df..01b7fd6304 100644 --- a/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRInstanceListSource.java +++ b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRInstanceListSource.java @@ -18,33 +18,22 @@ package org.mycore.common.config.instantiator.source; -import static org.mycore.common.config.instantiator.MCRInstantiatorUtils.createInstance; -import static org.mycore.common.config.instantiator.MCRInstantiatorUtils.emptyException; -import static org.mycore.common.config.instantiator.MCRInstantiatorUtils.orderedKeys; -import static org.mycore.common.config.instantiator.MCRInstantiatorUtils.property; - -import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.Set; import org.mycore.common.config.annotation.MCRInstanceList; -import org.mycore.common.config.annotation.MCRSentinel; -import org.mycore.common.config.instantiator.MCRInstanceConfiguration; import org.mycore.common.config.instantiator.target.MCRTarget; /** * A {@link MCRInstanceListSource} is a {@link MCRSource} that interprets a {@link MCRInstanceList}. */ -final class MCRInstanceListSource implements MCRSource { +final class MCRInstanceListSource extends MCRValueListSourceBase { private final MCRInstanceList annotation; - private final MCRSentinel sentinel; - MCRInstanceListSource(MCRInstanceList annotation, MCRAnnotationProvider annotationProvider) { + super(annotationProvider, new MCRInstanceExtractor(annotation.valueClass())); this.annotation = annotation; - this.sentinel = annotationProvider.get(MCRSentinel.class); } @Override @@ -73,29 +62,38 @@ public Class valueClass() { } @Override - public List get(MCRInstanceConfiguration configuration, MCRTarget target) { + protected String description() { + return "instance list"; + } - Map> nestedConfigurationMap = - configuration.nestedMap(annotation.valueClass(), annotation.name()); + @Override + protected String name() { + return annotation.name(); + } - List keyList = orderedKeys(property(configuration, annotation.name()), target, - nestedConfigurationMap, "instance list"); + @Override + protected String defaultName() { + return ""; + } - List instanceList = new ArrayList<>(nestedConfigurationMap.size()); - for (String key : keyList) { - MCRInstanceConfiguration nestedConfiguration = nestedConfigurationMap.get(key); - Object instance = createInstance(target, nestedConfiguration, sentinel, "instance list element"); - if (instance != null) { - instanceList.add(instance); - } - } + @Override + protected boolean supportsEmptyName() { + return true; + } - if (instanceList.isEmpty() && annotation.required()) { - throw emptyException(property(configuration, annotation.name()), target, "instance list"); - } + @Override + protected boolean absoluteName() { + return false; + } - return instanceList; + @Override + protected boolean supportsShortForm() { + return false; + } + @Override + protected boolean required() { + return annotation.required(); } } diff --git a/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRInstanceMapSource.java b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRInstanceMapSource.java index 05b369675c..173e22b4db 100644 --- a/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRInstanceMapSource.java +++ b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRInstanceMapSource.java @@ -18,31 +18,22 @@ package org.mycore.common.config.instantiator.source; -import static org.mycore.common.config.instantiator.MCRInstantiatorUtils.createInstance; -import static org.mycore.common.config.instantiator.MCRInstantiatorUtils.emptyException; -import static org.mycore.common.config.instantiator.MCRInstantiatorUtils.property; - -import java.util.HashMap; import java.util.Map; import java.util.Set; import org.mycore.common.config.annotation.MCRInstanceMap; -import org.mycore.common.config.annotation.MCRSentinel; -import org.mycore.common.config.instantiator.MCRInstanceConfiguration; import org.mycore.common.config.instantiator.target.MCRTarget; /** * A {@link MCRInstanceMapSource} is a {@link MCRSource} that interprets a {@link MCRInstanceMap}. */ -final class MCRInstanceMapSource implements MCRSource { +final class MCRInstanceMapSource extends MCRValueMapSourceBase { private final MCRInstanceMap annotation; - private final MCRSentinel sentinel; - MCRInstanceMapSource(MCRInstanceMap annotation, MCRAnnotationProvider annotationProvider) { + super(annotationProvider, new MCRInstanceExtractor(annotation.valueClass())); this.annotation = annotation; - this.sentinel = annotationProvider.get(MCRSentinel.class); } @Override @@ -71,26 +62,38 @@ public Class valueClass() { } @Override - public Map get(MCRInstanceConfiguration configuration, MCRTarget target) { + protected String description() { + return "instance map"; + } - Map> nestedConfigurationMap = - configuration.nestedMap(annotation.valueClass(), annotation.name()); + @Override + protected String name() { + return annotation.name(); + } - Map instanceMap = new HashMap<>(); - for (String key : nestedConfigurationMap.keySet()) { - MCRInstanceConfiguration nestedConfiguration = nestedConfigurationMap.get(key); - Object instance = createInstance(target, nestedConfiguration, sentinel, "instance map entry"); - if (instance != null) { - instanceMap.put(key, instance); - } - } + @Override + protected String defaultName() { + return ""; + } - if (instanceMap.isEmpty() && annotation.required()) { - throw emptyException(property(configuration, annotation.name()), target, "instance map"); - } + @Override + protected boolean supportsEmptyName() { + return true; + } - return instanceMap; + @Override + protected boolean absoluteName() { + return false; + } + @Override + protected boolean supportsShortForm() { + return false; + } + + @Override + protected boolean required() { + return annotation.required(); } } diff --git a/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRInstanceSource.java b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRInstanceSource.java index 1960a96fb8..54512cceb3 100644 --- a/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRInstanceSource.java +++ b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRInstanceSource.java @@ -18,33 +18,21 @@ package org.mycore.common.config.instantiator.source; -import static org.mycore.common.config.instantiator.MCRInstantiatorUtils.createInstance; -import static org.mycore.common.config.instantiator.MCRInstantiatorUtils.emptyNameException; -import static org.mycore.common.config.instantiator.MCRInstantiatorUtils.missingException; -import static org.mycore.common.config.instantiator.MCRInstantiatorUtils.property; - import java.util.Set; import org.mycore.common.config.annotation.MCRInstance; -import org.mycore.common.config.annotation.MCRSentinel; -import org.mycore.common.config.instantiator.MCRInstanceConfiguration; import org.mycore.common.config.instantiator.target.MCRTarget; /** * A {@link MCRInstanceSource} is a {@link MCRSource} that interprets a {@link MCRInstance}. */ -@SuppressWarnings({ "PMD.MCR.Singleton.ClassModifiers", "PMD.MCR.Singleton.PrivateConstructor", - "PMD.MCR.Singleton.NonPrivateConstructors", "PMD.MCR.Singleton.MethodModifiers", - "PMD.MCR.Singleton.MethodReturnType", "PMD.SingletonClassReturningNewInstance" }) -final class MCRInstanceSource implements MCRSource { +final class MCRInstanceSource extends MCRValueSourceBase { private final MCRInstance annotation; - private final MCRSentinel sentinel; - MCRInstanceSource(MCRInstance annotation, MCRAnnotationProvider annotationProvider) { + super(annotationProvider, new MCRInstanceExtractor(annotation.valueClass())); this.annotation = annotation; - this.sentinel = annotationProvider.get(MCRSentinel.class); } @Override @@ -73,22 +61,33 @@ public Class valueClass() { } @Override - public Object get(MCRInstanceConfiguration configuration, MCRTarget target) { + protected String description() { + return "instance"; + } - String name = annotation.name(); - if (name.isEmpty()) { - throw emptyNameException(target); - } + @Override + protected String name() { + return annotation.name(); + } - MCRInstanceConfiguration nestedConfiguration = configuration.nested(annotation.valueClass(), name); - Object instance = createInstance(target, nestedConfiguration, sentinel, "instance"); + @Override + protected String defaultName() { + return ""; + } - if (instance == null && annotation.required()) { - throw missingException(property(configuration, annotation.name()), target, "instance"); - } + @Override + protected boolean supportsEmptyName() { + return false; + } - return instance; + @Override + protected boolean absoluteName() { + return false; + } + @Override + protected boolean required() { + return annotation.required(); } } diff --git a/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRPropertyExtractor.java b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRPropertyExtractor.java new file mode 100644 index 0000000000..86c9198fdc --- /dev/null +++ b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRPropertyExtractor.java @@ -0,0 +1,36 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.common.config.instantiator.source; + +import java.util.Map; + +/** + * A {@link MCRClassPropertyExtractor} is a {@link MCRPropertyExtractor} that extracts {@link String} values. + */ +final class MCRPropertyExtractor implements MCRValueExtractor { + + @Override + public String toValue(MCRSourceContext context, Map properties, + Map fullProperties) { + + return properties.get(""); + + } + +} diff --git a/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRPropertyListSource.java b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRPropertyListSource.java index 99e7baf9ee..a30b93c629 100644 --- a/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRPropertyListSource.java +++ b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRPropertyListSource.java @@ -18,38 +18,22 @@ package org.mycore.common.config.instantiator.source; -import static org.mycore.common.config.instantiator.MCRInstantiatorUtils.emptyException; -import static org.mycore.common.config.instantiator.MCRInstantiatorUtils.orderedKeys; - -import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.mycore.common.config.MCRConfiguration2; import org.mycore.common.config.annotation.MCRPropertyList; -import org.mycore.common.config.annotation.MCRSentinel; -import org.mycore.common.config.instantiator.MCRInstanceConfiguration; import org.mycore.common.config.instantiator.target.MCRTarget; /** * A {@link MCRPropertyListSource} is a {@link MCRSource} that interprets a {@link MCRPropertyList}. */ -final class MCRPropertyListSource implements MCRSource { - - private static final Logger LOGGER = LogManager.getLogger(); +final class MCRPropertyListSource extends MCRValueListSourceBase { private final MCRPropertyList annotation; - private final MCRSentinel sentinel; - MCRPropertyListSource(MCRPropertyList annotation, MCRAnnotationProvider annotationProvider) { + super(annotationProvider, new MCRPropertyExtractor()); this.annotation = annotation; - this.sentinel = annotationProvider.get(MCRSentinel.class); } @Override @@ -78,121 +62,38 @@ public Class valueClass() { } @Override - public List get(MCRInstanceConfiguration configuration, MCRTarget target) { - - Map fullProperties = configuration.fullProperties(); - - String property; - String description; - List propertyList; - if (annotation.absolute()) { - property = annotation.name(); - description = "absolute property list"; - Map properties = fullProperties; - propertyList = getPropertyList(property, annotation.name(), ".", target, properties, description); - } else { - if (annotation.name().isEmpty()) { - property = configuration.name().canonical(); - description = "property list"; - Map properties = configuration.properties(); - propertyList = getPropertyList(property, "", "", target, properties, description); - } else { - property = configuration.name().canonical() + "." + annotation.name(); - description = "property list"; - Map properties = configuration.properties(); - propertyList = getPropertyList(property, annotation.name(), ".", target, properties, description); - } - } - - String defaultName = annotation.defaultName(); - if (propertyList == null && !defaultName.isEmpty()) { - - property = defaultName; - description = "default property list"; - propertyList = getPropertyList(defaultName, defaultName, ".", target, fullProperties, description); - - if (propertyList == null || (propertyList.isEmpty() && annotation.required())) { - throw emptyException(property, target, description); - } - - } - - if ((propertyList == null || propertyList.isEmpty()) && annotation.required()) { - throw emptyException(property, target, description); - } - - return propertyList == null ? new ArrayList<>() : propertyList; + protected String description() { + return "property list"; + } + @Override + protected String name() { + return annotation.name(); } - private List getPropertyList(String property, String prefix, String delimiter, - MCRTarget target, Map properties, String description) { - - AtomicBoolean hasRelevantProperty = new AtomicBoolean(false); - - Map rawPropertyMap = new HashMap<>(); - String keyPrefix = prefix + delimiter; - int keyPrefixLength = keyPrefix.length(); - properties.forEach((key, value) -> { - if (key.startsWith(keyPrefix) && !key.isEmpty()) { - int index = key.indexOf('.', keyPrefixLength); - if (index == -1) { - if (!value.isEmpty()) { - hasRelevantProperty.set(true); - rawPropertyMap.put(key.substring(keyPrefixLength), value); - } - } - } - }); - - List headPropertyList = new ArrayList<>(0); - List tailPropertyList = new ArrayList<>(rawPropertyMap.size()); - - List keyList = orderedKeys(property, target, rawPropertyMap, description); - for (String key : keyList) { - String value = rawPropertyMap.get(key); - if (sentinel != null) { - boolean sentinelValue = sentinel.defaultValue(); - String configuredSentinelValue = properties.get(keyPrefix + key + "." + sentinel.name()); - if (configuredSentinelValue != null) { - sentinelValue = Boolean.parseBoolean(configuredSentinelValue); - } - if (sentinelValue == sentinel.rejectionValue()) { - if (LOGGER.isInfoEnabled()) { - LOGGER.info("[SENTINEL] Ignoring {} element {}.{} and all sub-properties", - description, property, key); - } - continue; - } - } - - if (key.charAt(0) == '-') { - headPropertyList.add(value); - } else { - tailPropertyList.add(value); - } - - } - - List shortFormList = List.of(); - String shortFormProperty = properties.get(prefix); - if (shortFormProperty != null) { - hasRelevantProperty.set(true); - shortFormList = parseShortFormList(shortFormProperty); - } - - int totalSize = headPropertyList.size() + shortFormList.size() + tailPropertyList.size(); - List fullPropertyList = new ArrayList<>(totalSize); - fullPropertyList.addAll(headPropertyList); - fullPropertyList.addAll(shortFormList); - fullPropertyList.addAll(tailPropertyList); - - return hasRelevantProperty.get() ? fullPropertyList : null; + @Override + protected String defaultName() { + return annotation.defaultName(); + } + @Override + protected boolean supportsEmptyName() { + return true; } - private List parseShortFormList(String value) { - return MCRConfiguration2.splitValue(value).toList(); + @Override + protected boolean absoluteName() { + return annotation.absolute(); + } + + @Override + protected boolean supportsShortForm() { + return true; + } + + @Override + protected boolean required() { + return annotation.required(); } } diff --git a/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRPropertyMapSource.java b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRPropertyMapSource.java index ae1da1cb00..54a95976c5 100644 --- a/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRPropertyMapSource.java +++ b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRPropertyMapSource.java @@ -18,36 +18,22 @@ package org.mycore.common.config.instantiator.source; -import static org.mycore.common.config.instantiator.MCRInstantiatorUtils.emptyException; - -import java.util.HashMap; import java.util.Map; import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.stream.Collectors; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.mycore.common.config.MCRConfiguration2; import org.mycore.common.config.annotation.MCRPropertyMap; -import org.mycore.common.config.annotation.MCRSentinel; -import org.mycore.common.config.instantiator.MCRInstanceConfiguration; import org.mycore.common.config.instantiator.target.MCRTarget; /** * A {@link MCRPropertyMapSource} is a {@link MCRSource} that interprets a {@link MCRPropertyMap}. */ -final class MCRPropertyMapSource implements MCRSource { - - private static final Logger LOGGER = LogManager.getLogger(); +final class MCRPropertyMapSource extends MCRValueMapSourceBase { private final MCRPropertyMap annotation; - private final MCRSentinel sentinel; - MCRPropertyMapSource(MCRPropertyMap annotation, MCRAnnotationProvider annotationProvider) { + super(annotationProvider, new MCRPropertyExtractor()); this.annotation = annotation; - this.sentinel = annotationProvider.get(MCRSentinel.class); } @Override @@ -76,113 +62,38 @@ public Class valueClass() { } @Override - public Map get(MCRInstanceConfiguration configuration, MCRTarget target) { - - Map fullProperties = configuration.fullProperties(); - - String property; - String description; - Map propertyMap; - if (annotation.absolute()) { - property = annotation.name(); - description = "absolute property map"; - Map properties = fullProperties; - propertyMap = getPropertyMap(property, annotation.name(), ".", properties, description); - } else { - if (annotation.name().isEmpty()) { - property = configuration.name().canonical(); - description = "property map"; - Map properties = configuration.properties(); - propertyMap = getPropertyMap(property, "", "", properties, description); - } else { - property = configuration.name().canonical() + "." + annotation.name(); - description = "property map"; - Map properties = configuration.properties(); - propertyMap = getPropertyMap(property, annotation.name(), ".", properties, description); - } - } - - String defaultName = annotation.defaultName(); - if (propertyMap == null && !defaultName.isEmpty()) { - - property = defaultName; - description = "default property map"; - propertyMap = getPropertyMap(defaultName, defaultName, ".", fullProperties, description); - - if (propertyMap == null || (propertyMap.isEmpty() && annotation.required())) { - throw emptyException(property, target, description); - } - - } - - if ((propertyMap == null || propertyMap.isEmpty()) && annotation.required()) { - throw emptyException(property, target, description); - } - - return propertyMap == null ? new HashMap<>() : propertyMap; + protected String description() { + return "property map"; + } + @Override + protected String name() { + return annotation.name(); } - private Map getPropertyMap(String property, String prefix, String delimiter, - Map properties, String description) { - - AtomicBoolean hasRelevantProperty = new AtomicBoolean(false); - - Map shortFormMap = Map.of(); - String shortFormProperty = properties.get(prefix); - if (shortFormProperty != null) { - hasRelevantProperty.set(true); - shortFormMap = parseShortFormMap(shortFormProperty); - } - - Map rawPropertyMap = new HashMap<>(shortFormMap); - String keyPrefix = prefix + delimiter; - int keyPrefixLength = keyPrefix.length(); - properties.forEach((key, value) -> { - if (key.startsWith(keyPrefix) && !key.isEmpty()) { - int index = key.indexOf('.', keyPrefixLength); - if (index == -1) { - if (!value.isEmpty()) { - hasRelevantProperty.set(true); - rawPropertyMap.put(key.substring(keyPrefixLength), value); - } - } - } - }); - - Map propertyMap = new HashMap<>(); - - for (String key : rawPropertyMap.keySet()) { - String value = rawPropertyMap.get(key); - if (sentinel != null) { - boolean sentinelValue = sentinel.defaultValue(); - String configuredSentinelValue = properties.get(keyPrefix + key + "." + sentinel.name()); - if (configuredSentinelValue != null) { - sentinelValue = Boolean.parseBoolean(configuredSentinelValue); - } - if (sentinelValue == sentinel.rejectionValue()) { - if (LOGGER.isInfoEnabled()) { - LOGGER.info("[SENTINEL] Ignoring {} entry {}.{} and all sub-properties", - description, property, key); - } - continue; - } - } - - propertyMap.put(key, value); - - } - - return hasRelevantProperty.get() ? propertyMap : null; + @Override + protected String defaultName() { + return annotation.defaultName(); + } + @Override + protected boolean supportsEmptyName() { + return true; } - private Map parseShortFormMap(String value) { - return MCRConfiguration2.splitValue(value) - .map(s -> s.split(":", 2)) - .filter(parts -> parts.length != 1) - .filter(parts -> !parts[1].isBlank()) - .collect(Collectors.toMap(parts -> parts[0].trim(), parts -> parts[1].trim())); + @Override + protected boolean absoluteName() { + return annotation.absolute(); + } + + @Override + protected boolean supportsShortForm() { + return true; + } + + @Override + protected boolean required() { + return annotation.required(); } } diff --git a/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRPropertySource.java b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRPropertySource.java index a76d817746..a19e79d1cc 100644 --- a/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRPropertySource.java +++ b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRPropertySource.java @@ -18,33 +18,21 @@ package org.mycore.common.config.instantiator.source; -import static org.mycore.common.config.instantiator.MCRInstantiatorUtils.emptyNameException; -import static org.mycore.common.config.instantiator.MCRInstantiatorUtils.missingException; - -import java.util.Map; import java.util.Set; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.mycore.common.config.annotation.MCRProperty; -import org.mycore.common.config.annotation.MCRSentinel; -import org.mycore.common.config.instantiator.MCRInstanceConfiguration; import org.mycore.common.config.instantiator.target.MCRTarget; /** * A {@link MCRPropertySource} is a {@link MCRSource} that interprets a {@link MCRProperty}. */ -final class MCRPropertySource implements MCRSource { - - private static final Logger LOGGER = LogManager.getLogger(); +final class MCRPropertySource extends MCRValueSourceBase { private final MCRProperty annotation; - private final MCRSentinel sentinel; - MCRPropertySource(MCRProperty annotation, MCRAnnotationProvider annotationProvider) { + super(annotationProvider, new MCRPropertyExtractor()); this.annotation = annotation; - this.sentinel = annotationProvider.get(MCRSentinel.class); } @Override @@ -73,69 +61,33 @@ public Class valueClass() { } @Override - public String get(MCRInstanceConfiguration configuration, MCRTarget target) { - - String name = annotation.name(); - if (name.isEmpty()) { - throw emptyNameException(target); - } - - Map fullProperties = configuration.fullProperties(); - - String property; - String description; - String propertyValue; - if (annotation.absolute()) { - property = annotation.name(); - description = "absolute property"; - propertyValue = getPropertyValue(property, annotation.name(), fullProperties, description); - } else { - property = configuration.name().canonical() + "." + annotation.name(); - description = "property"; - propertyValue = getPropertyValue(property, annotation.name(), configuration.properties(), description); - } - - String defaultName = annotation.defaultName(); - if (propertyValue == null && !defaultName.isEmpty()) { - - property = defaultName; - description = "default property"; - propertyValue = getPropertyValue(defaultName, defaultName, fullProperties, description); - - if (propertyValue == null) { - throw missingException(property, target, description); - } - - } - - if (propertyValue == null && annotation.required()) { - throw missingException(property, target, description); - } - - return propertyValue; - + protected String description() { + return "property"; } - private String getPropertyValue(String property, String prefix, - Map properties, String description) { + @Override + protected String name() { + return annotation.name(); + } - if (sentinel != null) { - boolean sentinelValue = sentinel.defaultValue(); - String configuredSentinelValue = properties.get(prefix + "." + sentinel.name()); - if (configuredSentinelValue != null) { - sentinelValue = Boolean.parseBoolean(configuredSentinelValue); - } - if (sentinelValue == sentinel.rejectionValue()) { - if (LOGGER.isInfoEnabled()) { - LOGGER.info("[SENTINEL] Ignoring {} {} and all sub-properties", description, property); - } - return null; - } + @Override + protected String defaultName() { + return annotation.defaultName(); + } - } + @Override + protected boolean supportsEmptyName() { + return false; + } - return properties.get(prefix); + @Override + protected boolean absoluteName() { + return annotation.absolute(); + } + @Override + protected boolean required() { + return annotation.required(); } } diff --git a/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRRawPropertiesSource.java b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRRawPropertiesSource.java index a082764865..bd904d5294 100644 --- a/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRRawPropertiesSource.java +++ b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRRawPropertiesSource.java @@ -18,8 +18,6 @@ package org.mycore.common.config.instantiator.source; -import static org.mycore.common.config.instantiator.MCRInstantiatorUtils.emptyRawException; - import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -27,6 +25,7 @@ import org.mycore.common.config.MCRConfigurationException; import org.mycore.common.config.annotation.MCRRawProperties; import org.mycore.common.config.instantiator.MCRInstanceConfiguration; +import org.mycore.common.config.instantiator.MCRInstantiatorUtils; import org.mycore.common.config.instantiator.target.MCRTarget; /** @@ -98,7 +97,7 @@ public Map get(MCRInstanceConfiguration configuration, MCRTar property = configuration.name().canonical() + "." + annotation.namePattern(); description = "raw property map"; } - throw emptyRawException(property, target, description); + throw MCRInstantiatorUtils.emptyRawException(property, target, description); } return filteredProperties; diff --git a/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRSource.java b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRSource.java index 74e1b4d261..098e02e2fb 100644 --- a/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRSource.java +++ b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRSource.java @@ -28,6 +28,9 @@ import java.util.function.BiFunction; import java.util.function.Function; +import org.mycore.common.config.annotation.MCRClassProperty; +import org.mycore.common.config.annotation.MCRClassPropertyList; +import org.mycore.common.config.annotation.MCRClassPropertyMap; import org.mycore.common.config.annotation.MCRInstance; import org.mycore.common.config.annotation.MCRInstanceList; import org.mycore.common.config.annotation.MCRInstanceMap; @@ -43,8 +46,7 @@ * Common abstraction for components of a class (i.e., a {@link Field} or a {@link Method}) that can, * in principle, be used for injection. It provides an abstraction for obtaining a value based on present annotations. */ -public sealed interface MCRSource permits MCRInstanceListSource, MCRInstanceMapSource, MCRInstanceSource, - MCRPostConstructionSource, MCRPropertyListSource, MCRPropertyMapSource, MCRPropertySource, MCRRawPropertiesSource { +public interface MCRSource { Type type(); @@ -72,6 +74,18 @@ enum Type { Group.VALUE_INJECTION, new Mapper<>(MCRPropertyList.class, MCRPropertyListSource::new)), + CLASS_PROPERTY( + Group.VALUE_INJECTION, + new Mapper<>(MCRClassProperty.class, MCRClassPropertySource::new)), + + CLASS_PROPERTY_MAP( + Group.VALUE_INJECTION, + new Mapper<>(MCRClassPropertyMap.class, MCRClassPropertyMapSource::new)), + + CLASS_PROPERTY_LIST( + Group.VALUE_INJECTION, + new Mapper<>(MCRClassPropertyList.class, MCRClassPropertyListSource::new)), + RAW_PROPERTIES( Group.VALUE_INJECTION, new Mapper<>(MCRRawProperties.class, MCRRawPropertiesSource::new)), diff --git a/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRSourceBase.java b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRSourceBase.java new file mode 100644 index 0000000000..dc0f7a6ec8 --- /dev/null +++ b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRSourceBase.java @@ -0,0 +1,170 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.common.config.instantiator.source; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.mycore.common.config.MCRConfigurationException; +import org.mycore.common.config.annotation.MCRSentinel; +import org.mycore.common.config.instantiator.MCRInstanceConfiguration; +import org.mycore.common.config.instantiator.MCRInstantiatorUtils; +import org.mycore.common.config.instantiator.target.MCRTarget; + +/** + * A {@link MCRSourceBase} is a base implementation of {@link MCRSource} that + * handles basic aspects for obtaining a value for annotation based injection from properties, i.e. + *
    + *
  • whether annotation name are allowed
  • + *
  • whether the annotation name is absolute or not,
  • + *
  • whether falling back to a default value is necessary, if no value is configured
  • + *
  • whether an exception is thrown id no value and no default value is configured.
  • + *
+ * + * @param the type of injected value. + */ +abstract class MCRSourceBase implements MCRSource { + + protected final Logger logger = LogManager.getLogger(getClass()); + + @Override + @SuppressWarnings("PMD.NPathComplexity") + public final Result get(MCRInstanceConfiguration configuration, MCRTarget target) { + + String name = name(); + Map fullProperties = configuration.fullProperties(); + + MCRSourceContext context; + Result result; + + if (name.isEmpty() && (absoluteName() || !supportsEmptyName())) { + throw MCRInstantiatorUtils.emptyNameException(target); + } + + if (absoluteName()) { + context = new MCRSourceContext(target, name, "absolute " + description()); + result = getResult(context, reduceProperties(fullProperties, name), fullProperties); + } else if (supportsEmptyName() && name.isEmpty()) { + context = new MCRSourceContext(target, configuration.name().canonical(), description()); + result = getResult(context, configuration.properties(), fullProperties); + } else { + context = new MCRSourceContext(target, configuration.name().canonical() + "." + name, description()); + result = getResult(context, reduceProperties(configuration.properties(), name), fullProperties); + } + + String defaultName = defaultName(); + if (isMissingResult(result) && !defaultName.isEmpty()) { + context = new MCRSourceContext(target, defaultName, "default " + description()); + result = getResult(context, reduceProperties(fullProperties, defaultName), fullProperties); + } + + if (isMissingResult(result) && required()) { + throw missingResultException(context); + } + + return isMissingResult(result) ? missingResultReplacement() : result; + + } + + protected static Map reduceProperties(Map properties, String prefix) { + + final String prefixWithDelimiter = prefix + '.'; + final int prefixWithDelimiterLength = prefixWithDelimiter.length(); + + Map reducedProperties = new HashMap<>(); + for (Map.Entry 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; + } + + protected static Set nextNestedKeys(Map properties) { + Set keys = new HashSet<>(); + properties.keySet().forEach((key) -> { + if (!key.isEmpty()) { + int index = key.indexOf('.'); + if (index == -1) { + keys.add(key); + } else { + keys.add(key.substring(0, index)); + } + } + }); + return keys; + } + + protected abstract String description(); + + protected abstract String name(); + + protected abstract String defaultName(); + + protected abstract boolean supportsEmptyName(); + + protected abstract boolean absoluteName(); + + protected abstract Result getResult(MCRSourceContext context, Map properties, + Map fullProperties); + + protected abstract boolean required(); + + protected abstract boolean isMissingResult(Result result); + + protected abstract MCRConfigurationException missingResultException(MCRSourceContext context); + + protected abstract Result missingResultReplacement(); + + protected final boolean rejectedBySentinel(MCRSentinel sentinel, MCRSourceContext context, + Map properties) { + + if (sentinel != null) { + boolean sentinelValue = sentinel.defaultValue(); + String configuredSentinelValue = properties.get(sentinel.name()); + if (configuredSentinelValue != null) { + sentinelValue = Boolean.parseBoolean(configuredSentinelValue); + } + if (sentinelValue == sentinel.rejectionValue()) { + if (logger.isInfoEnabled()) { + logger.info("[SENTINEL] Ignoring {}, configured in {} (and sub-properties thereof), " + + "because {}.{} has value {}", context.description(), context.property(), + context.property(), sentinel.name(), sentinel.rejectionValue()); + } + return true; + } + + } + + return false; + + } + +} diff --git a/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRSourceContext.java b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRSourceContext.java new file mode 100644 index 0000000000..818348437c --- /dev/null +++ b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRSourceContext.java @@ -0,0 +1,136 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.common.config.instantiator.source; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Locale; +import java.util.SortedMap; +import java.util.TreeMap; + +import org.mycore.common.config.MCRConfigurationException; +import org.mycore.common.config.instantiator.MCRInstantiatorUtils; +import org.mycore.common.config.instantiator.target.MCRTarget; + +public final class MCRSourceContext { + + private final MCRTarget target; + + private final String property; + + private final String description; + + private final List hints; + + public MCRSourceContext(MCRTarget target, String property, String description, String... hints) { + this(target, property, description, List.of(hints)); + } + + private MCRSourceContext(MCRTarget target, String property, String description, List hints) { + this.target = target; + this.property = property; + this.description = description; + this.hints = hints; + } + + public MCRTarget target() { + return target; + } + + public String property() { + return property; + } + + public String description() { + return description + (hints.isEmpty() ? "" : " (" + String.join(", ", hints) + ")"); + } + + public List hints() { + return hints; + } + + public MCRSourceContext nested(String prefix, String description) { + return new MCRSourceContext(target, property + "." + prefix, description, hints); + } + + public List orderedKeys(Collection keys) { + + SortedMap keyMap = new TreeMap<>(); + for (String key : keys) { + try { + Integer integerValue = Integer.parseInt(key); + String alreadyMappedKey = keyMap.put(integerValue, key); + if (alreadyMappedKey != null && !alreadyMappedKey.equals(key)) { + throw inconsistentIntegerKeysException(key, alreadyMappedKey); + } + } catch (NumberFormatException exception) { + throw nonIntegerKeyException(key, exception); + } + } + + return new ArrayList<>(keyMap.values()); + + } + + public MCRConfigurationException configurationException(String exceptionMessage) { + return new MCRConfigurationException(exceptionMessage(exceptionMessage)); + } + + public MCRConfigurationException configurationException(String exceptionMessage, Exception exception) { + return new MCRConfigurationException(exceptionMessage(exceptionMessage), exception); + } + + public MCRConfigurationException missingException() { + return configurationException("is missing"); + } + + public MCRConfigurationException emptyException() { + return configurationException("is empty"); + } + + public MCRConfigurationException classLoadingException(String className, ClassNotFoundException exception) { + return configurationException("has a class (" + className + ") that could not be loaded", exception); + } + + public MCRConfigurationException classIncompatibilityException(Class annotatedClass, Class actualClass) { + return configurationException("has a class (" + actualClass.getName() + ") that is incompatible " + + "with the annotated class (" + annotatedClass.getName() + ")"); + } + + public MCRConfigurationException nonIntegerKeyException(String key, NumberFormatException exception) { + return configurationException("has element with non-integer key " + key, exception); + } + + public MCRConfigurationException inconsistentIntegerKeysException(String key1, String key2) { + return configurationException("has element with inconsistent integer keys " + key1 + " and " + key2); + } + + private String exceptionMessage(String exceptionMessage) { + return MCRInstantiatorUtils.capitalize(description()) + ", configured in " + property() + + " (and sub-properties thereof)," + " for target " + target.type().name().toLowerCase(Locale.ROOT) + " '" + + target.name() + "' in configured class " + target.declaringClass().getName() + " " + exceptionMessage; + } + + @Override + public String toString() { + return property() + " / " + description(); + } + +} diff --git a/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRValueExtractor.java b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRValueExtractor.java new file mode 100644 index 0000000000..f8ae329bc3 --- /dev/null +++ b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRValueExtractor.java @@ -0,0 +1,32 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.common.config.instantiator.source; + +import java.util.Map; + +/** + * A {@link MCRValueExtractor} obtains a value from properties. + * + * @param the type of value to be extracted. + */ +interface MCRValueExtractor { + + Value toValue(MCRSourceContext context, Map properties, Map fullProperties); + +} diff --git a/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRValueListSourceBase.java b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRValueListSourceBase.java new file mode 100644 index 0000000000..09ddf104c8 --- /dev/null +++ b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRValueListSourceBase.java @@ -0,0 +1,107 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.common.config.instantiator.source; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.mycore.common.config.MCRConfiguration2; +import org.mycore.common.config.MCRConfigurationException; +import org.mycore.common.config.annotation.MCRSentinel; + +/** + * A {@link MCRValueListSourceBase} is a base implementation of {@link MCRSource} that + * obtains a {@link List} of values for annotation based injection from properties. + * It provides support for {@link MCRSentinel} for each list element and + * uses a {@link MCRValueExtractor} to obtain each list element value from the corresponding nested properties. + * + * @param the type of injected map values. + */ +abstract class MCRValueListSourceBase extends MCRSourceBase> { + + private final MCRSentinel sentinel; + + private final MCRValueExtractor extractor; + + MCRValueListSourceBase(MCRAnnotationProvider annotationProvider, MCRValueExtractor extractor) { + this.sentinel = annotationProvider.get(MCRSentinel.class); + this.extractor = extractor; + } + + @Override + protected final List getResult(MCRSourceContext context, Map properties, + Map fullProperties) { + + List list = new ArrayList<>(); + + int negativeKeyCount = 0; + String elementDescription = context.description() + " element"; + for (String key : context.orderedKeys(nextNestedKeys(properties))) { + if (key.charAt(0) == '-') { + negativeKeyCount++; + } + MCRSourceContext nestedContext = context.nested(key, elementDescription); + Map nestesProperties = reduceProperties(properties, key); + if (!rejectedBySentinel(sentinel, nestedContext, nestesProperties)) { + Value value = extractor.toValue(nestedContext, nestesProperties, fullProperties); + if (value != null) { + list.add(value); + } + } + } + + String shortFormProperty = properties.get(""); + if (supportsShortForm() && shortFormProperty != null) { + List shortFormList = new ArrayList<>(); + for (String shortFormValue : parseShortFormList(shortFormProperty)) { + Value value = extractor.toValue(context, Map.of("", shortFormValue), fullProperties); + if (value != null) { + shortFormList.add(value); + } + } + list.addAll(negativeKeyCount, shortFormList); + } + + return list; + + } + + protected abstract boolean supportsShortForm(); + + private List parseShortFormList(String value) { + return MCRConfiguration2.splitValue(value).toList(); + } + + @Override + protected final boolean isMissingResult(List result) { + return result.isEmpty(); + } + + @Override + protected final MCRConfigurationException missingResultException(MCRSourceContext context) { + return context.emptyException(); + } + + @Override + protected final List missingResultReplacement() { + return new ArrayList<>(); + } + +} diff --git a/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRValueMapSourceBase.java b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRValueMapSourceBase.java new file mode 100644 index 0000000000..ee4dd83e2d --- /dev/null +++ b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRValueMapSourceBase.java @@ -0,0 +1,105 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.common.config.instantiator.source; + +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +import org.mycore.common.config.MCRConfiguration2; +import org.mycore.common.config.MCRConfigurationException; +import org.mycore.common.config.annotation.MCRSentinel; + +/** + * A {@link MCRValueMapSourceBase} is a base implementation of {@link MCRSource} that + * obtains a {@link Map} of values for annotation based injection from properties. + * It provides support for {@link MCRSentinel} for each map entry and + * uses a {@link MCRValueExtractor} to obtain each map entry value from the corresponding nested properties. + * + * @param the type of injected map values. + */ +abstract class MCRValueMapSourceBase extends MCRSourceBase> { + + private final MCRSentinel sentinel; + + private final MCRValueExtractor extractor; + + MCRValueMapSourceBase(MCRAnnotationProvider annotationProvider, MCRValueExtractor extractor) { + this.sentinel = annotationProvider.get(MCRSentinel.class); + this.extractor = extractor; + } + + @Override + protected final Map getResult(MCRSourceContext context, Map properties, + Map fullProperties) { + + Map map = new HashMap<>(); + + String shortFormProperty = properties.get(""); + if (supportsShortForm() && shortFormProperty != null) { + parseShortFormMap(shortFormProperty).forEach((key, shortFormValue) -> { + Value value = extractor.toValue(context, Map.of("", shortFormValue), fullProperties); + if (value != null) { + map.put(key, value); + } + }); + } + + String entryDescription = context.description() + " entry"; + for (String key : nextNestedKeys(properties)) { + MCRSourceContext nestedContext = context.nested(key, entryDescription); + Map nestesProperties = reduceProperties(properties, key); + if (!rejectedBySentinel(sentinel, nestedContext, nestesProperties)) { + Value value = extractor.toValue(nestedContext, nestesProperties, fullProperties); + if (value != null) { + map.put(key, value); + } + } + } + + return map; + + } + + protected abstract boolean supportsShortForm(); + + private Map parseShortFormMap(String value) { + return MCRConfiguration2.splitValue(value) + .map(s -> s.split(":", 2)) + .filter(parts -> parts.length != 1) + .filter(parts -> !parts[1].isBlank()) + .collect(Collectors.toMap(parts -> parts[0].trim(), parts -> parts[1].trim())); + } + + @Override + protected final boolean isMissingResult(Map result) { + return result.isEmpty(); + } + + @Override + protected final MCRConfigurationException missingResultException(MCRSourceContext context) { + return context.emptyException(); + } + + @Override + protected final Map missingResultReplacement() { + return new HashMap<>(); + } + +} diff --git a/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRValueSourceBase.java b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRValueSourceBase.java new file mode 100644 index 0000000000..2328096820 --- /dev/null +++ b/mycore-base/src/main/java/org/mycore/common/config/instantiator/source/MCRValueSourceBase.java @@ -0,0 +1,72 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.common.config.instantiator.source; + +import java.util.Map; + +import org.mycore.common.config.MCRConfigurationException; +import org.mycore.common.config.annotation.MCRSentinel; + +/** + * A {@link MCRValueSourceBase} is a base implementation of {@link MCRSource} that + * obtains a single value for annotation based injection from properties. + * It provides support for {@link MCRSentinel} and + * uses a {@link MCRValueExtractor} to obtain a single value from properties. + * + * @param the type of injected value. + */ +abstract class MCRValueSourceBase extends MCRSourceBase { + + private final MCRSentinel sentinel; + + private final MCRValueExtractor extractor; + + MCRValueSourceBase(MCRAnnotationProvider annotationProvider, MCRValueExtractor extractor) { + this.sentinel = annotationProvider.get(MCRSentinel.class); + this.extractor = extractor; + } + + @Override + protected final Value getResult(MCRSourceContext context, Map properties, + Map fullProperties) { + + if (rejectedBySentinel(sentinel, context, properties)) { + return null; + } + + return extractor.toValue(context, properties, fullProperties); + + } + + @Override + protected final boolean isMissingResult(Value result) { + return result == null; + } + + @Override + protected final MCRConfigurationException missingResultException(MCRSourceContext context) { + return context.missingException(); + } + + @Override + protected final Value missingResultReplacement() { + return null; + } + +} diff --git a/mycore-base/src/main/java/org/mycore/common/xsl/MCRParameterCollector.java b/mycore-base/src/main/java/org/mycore/common/xsl/MCRParameterCollector.java index ce1ca682c8..7d2d4824f6 100644 --- a/mycore-base/src/main/java/org/mycore/common/xsl/MCRParameterCollector.java +++ b/mycore-base/src/main/java/org/mycore/common/xsl/MCRParameterCollector.java @@ -437,7 +437,7 @@ static Map initializeSafeProperties() { PROPERTIES_CHANGE_LISTENER_ID.set(uuid); - MCRConfiguration2.getPropertiesMap().forEach((key, value) -> { + MCRConfiguration2.getRawProperties().forEach((key, value) -> { safeProperties.put(xmlSafe(key), value); }); diff --git a/mycore-base/src/main/java/org/mycore/datamodel/metadata/validator/MCREditorOutValidator.java b/mycore-base/src/main/java/org/mycore/datamodel/metadata/validator/MCREditorOutValidator.java index 2f09783770..d233076f88 100644 --- a/mycore-base/src/main/java/org/mycore/datamodel/metadata/validator/MCREditorOutValidator.java +++ b/mycore-base/src/main/java/org/mycore/datamodel/metadata/validator/MCREditorOutValidator.java @@ -139,7 +139,7 @@ private static Map getValidatorMap() { map.put(MCRMetaAccessRule.class.getSimpleName(), getObjectCheckInstance(MCRMetaAccessRule.class)); map.put(MCRMetaClassification.class.getSimpleName(), new MCRMetaClassificationCheck()); map.put(MCRMetaHistoryDate.class.getSimpleName(), new MCRMetaHistoryDateCheck()); - Map props = MCRConfiguration2.getPropertiesMap() + Map props = MCRConfiguration2.getRawProperties() .entrySet() .stream() .filter(p -> p.getKey().startsWith(CONFIG_PREFIX + "class.")) diff --git a/mycore-base/src/main/java/org/mycore/frontend/cli/MCRCommandLineInterface.java b/mycore-base/src/main/java/org/mycore/frontend/cli/MCRCommandLineInterface.java index 32de51b711..055f8af0c1 100644 --- a/mycore-base/src/main/java/org/mycore/frontend/cli/MCRCommandLineInterface.java +++ b/mycore-base/src/main/java/org/mycore/frontend/cli/MCRCommandLineInterface.java @@ -211,7 +211,7 @@ protected static void processCommand(String command) { * @return expanded command */ public static String expandCommand(final String command) { - StringSubstitutor strSubstitutor = new StringSubstitutor(MCRConfiguration2.getPropertiesMap()); + StringSubstitutor strSubstitutor = new StringSubstitutor(MCRConfiguration2.getRawProperties()); String expandedCommand = strSubstitutor.replace(command); if (!expandedCommand.equals(command)) { output(command + " --> " + expandedCommand); diff --git a/mycore-base/src/main/java/org/mycore/frontend/cli/MCRCommandManager.java b/mycore-base/src/main/java/org/mycore/frontend/cli/MCRCommandManager.java index 2b1e03e570..4e0e0c64c6 100644 --- a/mycore-base/src/main/java/org/mycore/frontend/cli/MCRCommandManager.java +++ b/mycore-base/src/main/java/org/mycore/frontend/cli/MCRCommandManager.java @@ -83,7 +83,7 @@ protected void initCommands() { /** Read internal and/or external commands */ protected void initConfiguredCommands(String type) { String prefix = "MCR.CLI.Classes." + type; - Stream> propsWithPrefix = MCRConfiguration2.getPropertiesMap() + Stream> propsWithPrefix = MCRConfiguration2.getRawProperties() .entrySet() .stream() .filter(e -> e.getKey().startsWith(prefix)); diff --git a/mycore-base/src/main/java/org/mycore/frontend/cli/MCRDeveloperCommands.java b/mycore-base/src/main/java/org/mycore/frontend/cli/MCRDeveloperCommands.java index 8397253177..1832740cca 100644 --- a/mycore-base/src/main/java/org/mycore/frontend/cli/MCRDeveloperCommands.java +++ b/mycore-base/src/main/java/org/mycore/frontend/cli/MCRDeveloperCommands.java @@ -110,7 +110,7 @@ public static void showMessages(String lang) { help = "Show configuration property with key {0}", order = 40) public static void showProperty(String key) { - String value = MCRConfiguration2.getPropertiesMap().get(key); + String value = MCRConfiguration2.getRawProperties().get(key); if (value == null) { LOGGER.info("Found no value for key {}", key); } else { @@ -136,7 +136,7 @@ public static void showProperties(String keyPrefix) { help = "Show all configuration properties", order = 60) public static void showAllProperties() { - Map values = MCRConfiguration2.getPropertiesMap(); + Map values = MCRConfiguration2.getRawProperties(); if (values.isEmpty()) { LOGGER.info("Found no values"); } else { diff --git a/mycore-base/src/main/java/org/mycore/frontend/cli/MCRObjectCommands.java b/mycore-base/src/main/java/org/mycore/frontend/cli/MCRObjectCommands.java index 5da64e7eb2..8ccc445899 100644 --- a/mycore-base/src/main/java/org/mycore/frontend/cli/MCRObjectCommands.java +++ b/mycore-base/src/main/java/org/mycore/frontend/cli/MCRObjectCommands.java @@ -977,7 +977,7 @@ private static void xslt(String objectId, String xslFilePath, boolean force) thr xmlReader.setEntityResolver(MCREntityResolver.getInstance()); SAXSource styleSource = new SAXSource(xmlReader, style.getInputSource()); Transformer transformer = transformerFactory.newTransformer(styleSource); - for (Entry property : MCRConfiguration2.getPropertiesMap().entrySet()) { + for (Entry property : MCRConfiguration2.getRawProperties().entrySet()) { transformer.setParameter(property.getKey(), property.getValue()); } transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); diff --git a/mycore-base/src/main/java/org/mycore/services/staticcontent/MCRObjectStaticContentGenerator.java b/mycore-base/src/main/java/org/mycore/services/staticcontent/MCRObjectStaticContentGenerator.java index 1d66b6f81a..d26a0d0089 100644 --- a/mycore-base/src/main/java/org/mycore/services/staticcontent/MCRObjectStaticContentGenerator.java +++ b/mycore-base/src/main/java/org/mycore/services/staticcontent/MCRObjectStaticContentGenerator.java @@ -97,7 +97,7 @@ static MCRObjectStaticContentGenerator obtainInstance(String id) { } public static List getContentGenerators() { - return MCRConfiguration2.getPropertiesMap() + return MCRConfiguration2.getRawProperties() .keySet() .stream() .filter(k -> k.startsWith(CONFIG_ID_PREFIX)) diff --git a/mycore-base/src/test/java/org/mycore/common/config/MCRInstanceConfigurationTest.java b/mycore-base/src/test/java/org/mycore/common/config/MCRInstanceConfigurationTest.java index c3cc1da1e1..762e9f663a 100644 --- a/mycore-base/src/test/java/org/mycore/common/config/MCRInstanceConfigurationTest.java +++ b/mycore-base/src/test/java/org/mycore/common/config/MCRInstanceConfigurationTest.java @@ -35,14 +35,6 @@ public class MCRInstanceConfigurationTest { private static final String TEST_CLASS = TestClass.class.getName(); - private static final String NESTED_TEST_CLASS = NestedTestClass.class.getName(); - - private static final String NESTED_TEST_CLASS_A = NestedTestClassA.class.getName(); - - private static final String NESTED_TEST_CLASS_B = NestedTestClassB.class.getName(); - - private static final String NESTED_TEST_CLASS_C = NestedTestClassC.class.getName(); - @Test public void configuration() { @@ -74,194 +66,6 @@ public void configurationMovesEntries() { } - @Test - public void nestedConfiguration() { - - Map properties = new HashMap<>(); - properties.put("Foo.Bar.Class", TEST_CLASS); - properties.put("Foo.Bar.Baz.Class", NESTED_TEST_CLASS); - - MCRInstanceConfiguration configuration = ofName(Object.class, "Foo.Bar", properties); - MCRInstanceConfiguration nestedConfiguration = configuration.nested(Object.class, "Baz"); - - System.out.println(configuration); - System.out.println(nestedConfiguration); - - assertEquals("Foo.Bar.Baz.Class", nestedConfiguration.name().actual()); - assertEquals("Foo.Bar.Baz", nestedConfiguration.name().canonical()); - assertEquals(NestedTestClass.class, nestedConfiguration.valueClass()); - assertEquals(properties, nestedConfiguration.fullProperties()); - - } - - @Test - public void nestedConfigurationMovesEntries() { - - Map properties = new HashMap<>(); - properties.put("Foo.Bar.Class", TEST_CLASS); - properties.put("Foo.Bar.Baz.Class", NESTED_TEST_CLASS); - properties.put("Foo.Bar.Baz.Key1", "Value1"); - properties.put("Foo.Bar.Baz.Key2", "Value2"); - - MCRInstanceConfiguration configuration = ofName(Object.class, "Foo.Bar", properties); - MCRInstanceConfiguration nestedConfiguration = configuration.nested(Object.class, "Baz"); - - assertEquals("Value1", nestedConfiguration.properties().get("Key1")); - assertEquals("Value2", nestedConfiguration.properties().get("Key2")); - assertEquals(2, nestedConfiguration.properties().size()); - - } - - @Test - public void nestedMap() { - - Map properties = new HashMap<>(); - properties.put("Foo.Bar.Class", TEST_CLASS); - properties.put("Foo.Bar.A.Class", NESTED_TEST_CLASS_A); - properties.put("Foo.Bar.B.Class", NESTED_TEST_CLASS_B); - properties.put("Foo.Bar.C.Class", NESTED_TEST_CLASS_C); - - MCRInstanceConfiguration configuration = ofName(Object.class, "Foo.Bar", properties); - Map> nestedConfigurations = configuration.nestedMap(Object.class); - MCRInstanceConfiguration nestedConfigurationA = nestedConfigurations.get("A"); - MCRInstanceConfiguration nestedConfigurationB = nestedConfigurations.get("B"); - MCRInstanceConfiguration nestedConfigurationC = nestedConfigurations.get("C"); - assertEquals(3, nestedConfigurations.size()); - - assertEquals("Foo.Bar.A.Class", nestedConfigurationA.name().actual()); - assertEquals("Foo.Bar.A", nestedConfigurationA.name().canonical()); - assertEquals(NestedTestClassA.class, nestedConfigurationA.valueClass()); - assertEquals(properties, nestedConfigurationA.fullProperties()); - - assertEquals("Foo.Bar.B.Class", nestedConfigurationB.name().actual()); - assertEquals("Foo.Bar.B", nestedConfigurationB.name().canonical()); - assertEquals(NestedTestClassB.class, nestedConfigurationB.valueClass()); - assertEquals(properties, nestedConfigurationB.fullProperties()); - - assertEquals("Foo.Bar.C.Class", nestedConfigurationC.name().actual()); - assertEquals("Foo.Bar.C", nestedConfigurationC.name().canonical()); - assertEquals(NestedTestClassC.class, nestedConfigurationC.valueClass()); - assertEquals(properties, nestedConfigurationC.fullProperties()); - - } - - @Test - public void nestedMapMovesEntries() { - - Map properties = new HashMap<>(); - properties.put("Foo.Bar.Class", TEST_CLASS); - properties.put("Foo.Bar.A.Class", NESTED_TEST_CLASS_A); - properties.put("Foo.Bar.A.Key1", "ValueA1"); - properties.put("Foo.Bar.A.Key2", "ValueA2"); - properties.put("Foo.Bar.B.Class", NESTED_TEST_CLASS_B); - properties.put("Foo.Bar.B.Key1", "ValueB1"); - properties.put("Foo.Bar.B.Key2", "ValueB2"); - properties.put("Foo.Bar.C.Class", NESTED_TEST_CLASS_C); - properties.put("Foo.Bar.C.Key1", "ValueC1"); - properties.put("Foo.Bar.C.Key2", "ValueC2"); - - MCRInstanceConfiguration configuration = ofName(Object.class, "Foo.Bar", properties); - Map> nestedConfigurations = configuration.nestedMap(Object.class); - MCRInstanceConfiguration nestedConfigurationA = nestedConfigurations.get("A"); - MCRInstanceConfiguration nestedConfigurationB = nestedConfigurations.get("B"); - MCRInstanceConfiguration nestedConfigurationC = nestedConfigurations.get("C"); - assertEquals(3, nestedConfigurations.size()); - - assertEquals("ValueA1", nestedConfigurationA.properties().get("Key1")); - assertEquals("ValueA2", nestedConfigurationA.properties().get("Key2")); - assertEquals(2, nestedConfigurationA.properties().size()); - - assertEquals("ValueB1", nestedConfigurationB.properties().get("Key1")); - assertEquals("ValueB2", nestedConfigurationB.properties().get("Key2")); - assertEquals(2, nestedConfigurationB.properties().size()); - - assertEquals("ValueC1", nestedConfigurationC.properties().get("Key1")); - assertEquals("ValueC2", nestedConfigurationC.properties().get("Key2")); - assertEquals(2, nestedConfigurationC.properties().size()); - - } - - @Test - public void nestedMapWithPrefix() { - - Map properties = new HashMap<>(); - properties.put("Foo.Bar.Class", TEST_CLASS); - properties.put("Foo.Bar.Baz.A.Class", NESTED_TEST_CLASS_A); - properties.put("Foo.Bar.Baz.B.Class", NESTED_TEST_CLASS_B); - properties.put("Foo.Bar.Baz.C.Class", NESTED_TEST_CLASS_C); - - MCRInstanceConfiguration configuration = - ofName(Object.class, "Foo.Bar", properties); - Map> nestedConfigurations = - configuration.nestedMap(Object.class, "Baz"); - MCRInstanceConfiguration nestedConfigurationA = - nestedConfigurations.get("A"); - MCRInstanceConfiguration nestedConfigurationB = - nestedConfigurations.get("B"); - MCRInstanceConfiguration nestedConfigurationC = - nestedConfigurations.get("C"); - - assertEquals(3, nestedConfigurations.size()); - - assertEquals("Foo.Bar.Baz.A.Class", nestedConfigurationA.name().actual()); - assertEquals("Foo.Bar.Baz.A", nestedConfigurationA.name().canonical()); - assertEquals(NestedTestClassA.class, nestedConfigurationA.valueClass()); - assertEquals(properties, nestedConfigurationA.fullProperties()); - - assertEquals("Foo.Bar.Baz.B.Class", nestedConfigurationB.name().actual()); - assertEquals("Foo.Bar.Baz.B", nestedConfigurationB.name().canonical()); - assertEquals(NestedTestClassB.class, nestedConfigurationB.valueClass()); - assertEquals(properties, nestedConfigurationB.fullProperties()); - - assertEquals("Foo.Bar.Baz.C.Class", nestedConfigurationC.name().actual()); - assertEquals("Foo.Bar.Baz.C", nestedConfigurationC.name().canonical()); - assertEquals(NestedTestClassC.class, nestedConfigurationC.valueClass()); - assertEquals(properties, nestedConfigurationC.fullProperties()); - - } - - @Test - public void nestedMapWithPrefixMovesEntries() { - - Map properties = new HashMap<>(); - properties.put("Foo.Bar.Class", TEST_CLASS); - properties.put("Foo.Bar.Baz.A.Class", NESTED_TEST_CLASS_A); - properties.put("Foo.Bar.Baz.A.Key1", "ValueA1"); - properties.put("Foo.Bar.Baz.A.Key2", "ValueA2"); - properties.put("Foo.Bar.Baz.B.Class", NESTED_TEST_CLASS_B); - properties.put("Foo.Bar.Baz.B.Key1", "ValueB1"); - properties.put("Foo.Bar.Baz.B.Key2", "ValueB2"); - properties.put("Foo.Bar.Baz.C.Class", NESTED_TEST_CLASS_C); - properties.put("Foo.Bar.Baz.C.Key1", "ValueC1"); - properties.put("Foo.Bar.Baz.C.Key2", "ValueC2"); - - MCRInstanceConfiguration configuration = - ofName(Object.class, "Foo.Bar", properties); - Map> nestedConfigurations = - configuration.nestedMap(Object.class, "Baz"); - MCRInstanceConfiguration nestedConfigurationA = - nestedConfigurations.get("A"); - MCRInstanceConfiguration nestedConfigurationB = - nestedConfigurations.get("B"); - MCRInstanceConfiguration nestedConfigurationC = - nestedConfigurations.get("C"); - - assertEquals(3, nestedConfigurations.size()); - - assertEquals("ValueA1", nestedConfigurationA.properties().get("Key1")); - assertEquals("ValueA2", nestedConfigurationA.properties().get("Key2")); - assertEquals(2, nestedConfigurationA.properties().size()); - - assertEquals("ValueB1", nestedConfigurationB.properties().get("Key1")); - assertEquals("ValueB2", nestedConfigurationB.properties().get("Key2")); - assertEquals(2, nestedConfigurationB.properties().size()); - - assertEquals("ValueC1", nestedConfigurationC.properties().get("Key1")); - assertEquals("ValueC2", nestedConfigurationC.properties().get("Key2")); - assertEquals(2, nestedConfigurationC.properties().size()); - - } - @Test public void directConfiguration() { @@ -295,206 +99,7 @@ public void directConfigurationRemovesClassEntry() { } - @Test - public void nestedDirectConfiguration() { - - Map properties = new HashMap<>(); - properties.put("Instance.Baz.Class", NESTED_TEST_CLASS); - - MCRInstanceConfiguration configuration = - ofClass(Object.class, TestClass.class, "Instance", properties); - MCRInstanceConfiguration nestedConfiguration = - configuration.nested(Object.class, "Baz"); - - assertEquals("Instance.Baz.Class", nestedConfiguration.name().actual()); - assertEquals("Instance.Baz", nestedConfiguration.name().canonical()); - assertEquals(NestedTestClass.class, nestedConfiguration.valueClass()); - assertEquals(properties, nestedConfiguration.fullProperties()); - - } - - @Test - public void nestedDirectConfigurationMap() { - - Map properties = new HashMap<>(); - properties.put("Instance.A.Class", NESTED_TEST_CLASS_A); - properties.put("Instance.B.Class", NESTED_TEST_CLASS_B); - properties.put("Instance.C.Class", NESTED_TEST_CLASS_C); - - MCRInstanceConfiguration configuration = - ofClass(Object.class, TestClass.class, "Instance", properties); - Map> nestedConfigurations = - configuration.nestedMap(Object.class); - MCRInstanceConfiguration nestedConfigurationA = - nestedConfigurations.get("A"); - MCRInstanceConfiguration nestedConfigurationB = - nestedConfigurations.get("B"); - MCRInstanceConfiguration nestedConfigurationC = - nestedConfigurations.get("C"); - - assertEquals(3, nestedConfigurations.size()); - - assertEquals("Instance.A.Class", nestedConfigurationA.name().actual()); - assertEquals("Instance.A", nestedConfigurationA.name().canonical()); - assertEquals(NestedTestClassA.class, nestedConfigurationA.valueClass()); - assertEquals(properties, nestedConfigurationA.fullProperties()); - - assertEquals("Instance.B.Class", nestedConfigurationB.name().actual()); - assertEquals("Instance.B", nestedConfigurationB.name().canonical()); - assertEquals(NestedTestClassB.class, nestedConfigurationB.valueClass()); - assertEquals(properties, nestedConfigurationB.fullProperties()); - - assertEquals("Instance.C.Class", nestedConfigurationC.name().actual()); - assertEquals("Instance.C", nestedConfigurationC.name().canonical()); - assertEquals(NestedTestClassC.class, nestedConfigurationC.valueClass()); - assertEquals(properties, nestedConfigurationC.fullProperties()); - - } - - @Test - public void nestedDirectConfigurationMapRemovesClassEntries() { - - Map properties = new HashMap<>(); - properties.put("Instance.A.Class", NESTED_TEST_CLASS_A); - properties.put("Instance.A.class", "ClassValue"); - properties.put("Instance.A", "ClassValue"); - properties.put("Instance.B.Class", NESTED_TEST_CLASS_B); - properties.put("Instance.B.class", "ClassValue"); - properties.put("Instance.B", "ClassValue"); - properties.put("Instance.C.Class", NESTED_TEST_CLASS_C); - properties.put("Instance.C.class", "ClassValue"); - properties.put("Instance.C", "ClassValue"); - - MCRInstanceConfiguration configuration = - ofClass(Object.class, TestClass.class, "Instance", properties); - Map> nestedConfigurations = - configuration.nestedMap(Object.class); - MCRInstanceConfiguration nestedConfigurationA = - nestedConfigurations.get("A"); - MCRInstanceConfiguration nestedConfigurationB = - nestedConfigurations.get("B"); - MCRInstanceConfiguration nestedConfigurationC = - nestedConfigurations.get("C"); - - assertEquals(3, nestedConfigurations.size()); - - assertFalse(nestedConfigurationA.properties().containsKey("Class")); - assertEquals("ClassValue", nestedConfigurationA.properties().get("class")); - assertEquals("ClassValue", nestedConfigurationA.properties().get("")); - assertEquals(2, nestedConfigurationA.properties().size()); - - assertFalse(nestedConfigurationB.properties().containsKey("Class")); - assertEquals("ClassValue", nestedConfigurationB.properties().get("class")); - assertEquals("ClassValue", nestedConfigurationB.properties().get("")); - assertEquals(2, nestedConfigurationB.properties().size()); - - assertFalse(nestedConfigurationC.properties().containsKey("Class")); - assertEquals("ClassValue", nestedConfigurationC.properties().get("class")); - assertEquals("ClassValue", nestedConfigurationC.properties().get("")); - assertEquals(2, nestedConfigurationC.properties().size()); - - } - - @Test - public void nestedDirectConfigurationMapWithPrefix() { - - Map properties = new HashMap<>(); - properties.put("Instance.Baz.A.Class", NESTED_TEST_CLASS_A); - properties.put("Instance.Baz.B.Class", NESTED_TEST_CLASS_B); - properties.put("Instance.Baz.C.Class", NESTED_TEST_CLASS_C); - - MCRInstanceConfiguration configuration = - ofClass(Object.class, TestClass.class, "Instance", properties); - Map> nestedConfigurations = - configuration.nestedMap(Object.class, "Baz"); - MCRInstanceConfiguration nestedConfigurationA = - nestedConfigurations.get("A"); - MCRInstanceConfiguration nestedConfigurationB = - nestedConfigurations.get("B"); - MCRInstanceConfiguration nestedConfigurationC = - nestedConfigurations.get("C"); - - assertEquals(3, nestedConfigurations.size()); - - assertEquals("Instance.Baz.A.Class", nestedConfigurationA.name().actual()); - assertEquals("Instance.Baz.A", nestedConfigurationA.name().canonical()); - assertEquals(NestedTestClassA.class, nestedConfigurationA.valueClass()); - assertEquals(properties, nestedConfigurationA.fullProperties()); - - assertEquals("Instance.Baz.B.Class", nestedConfigurationB.name().actual()); - assertEquals("Instance.Baz.B", nestedConfigurationB.name().canonical()); - assertEquals(NestedTestClassB.class, nestedConfigurationB.valueClass()); - assertEquals(properties, nestedConfigurationB.fullProperties()); - - assertEquals("Instance.Baz.C.Class", nestedConfigurationC.name().actual()); - assertEquals("Instance.Baz.C", nestedConfigurationC.name().canonical()); - assertEquals(NestedTestClassC.class, nestedConfigurationC.valueClass()); - assertEquals(properties, nestedConfigurationC.fullProperties()); - - } - - @Test - public void nestedDirectConfigurationMapWithPrefixRemovesClassEntries() { - - Map properties = new HashMap<>(); - properties.put("Instance.Baz.A.Class", NESTED_TEST_CLASS_A); - properties.put("Instance.Baz.A.class", "ClassValue"); - properties.put("Instance.Baz.A", "ClassValue"); - properties.put("Instance.Baz.B.Class", NESTED_TEST_CLASS_B); - properties.put("Instance.Baz.B.class", "ClassValue"); - properties.put("Instance.Baz.B", "ClassValue"); - properties.put("Instance.Baz.C.Class", NESTED_TEST_CLASS_C); - properties.put("Instance.Baz.C.class", "ClassValue"); - properties.put("Instance.Baz.C", "ClassValue"); - - MCRInstanceConfiguration configuration = - ofClass(Object.class, TestClass.class, "Instance", properties); - Map> nestedConfigurations = - configuration.nestedMap(Object.class, "Baz"); - MCRInstanceConfiguration nestedConfigurationA = - nestedConfigurations.get("A"); - MCRInstanceConfiguration nestedConfigurationB = - nestedConfigurations.get("B"); - MCRInstanceConfiguration nestedConfigurationC = - nestedConfigurations.get("C"); - - assertEquals(3, nestedConfigurations.size()); - - assertFalse(nestedConfigurationA.properties().containsKey("Class")); - assertEquals("ClassValue", nestedConfigurationA.properties().get("class")); - assertEquals("ClassValue", nestedConfigurationA.properties().get("")); - assertEquals(2, nestedConfigurationA.properties().size()); - - assertFalse(nestedConfigurationB.properties().containsKey("Class")); - assertEquals("ClassValue", nestedConfigurationB.properties().get("class")); - assertEquals("ClassValue", nestedConfigurationB.properties().get("")); - assertEquals(2, nestedConfigurationB.properties().size()); - - assertFalse(nestedConfigurationC.properties().containsKey("Class")); - assertEquals("ClassValue", nestedConfigurationC.properties().get("class")); - assertEquals("ClassValue", nestedConfigurationC.properties().get("")); - assertEquals(2, nestedConfigurationC.properties().size()); - - } - public static class TestClass { - - } - - public static class NestedTestClass { - - } - - public static class NestedTestClassA { - - } - - public static class NestedTestClassB { - - } - - public static class NestedTestClassC { - } } diff --git a/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorClassPropertyBasicTest.java b/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorClassPropertyBasicTest.java new file mode 100644 index 0000000000..0f0b55b5c7 --- /dev/null +++ b/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorClassPropertyBasicTest.java @@ -0,0 +1,149 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.common.config; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mycore.common.config.instantiator.MCRInstanceConfiguration.ofName; + +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.Test; +import org.mycore.common.MCRTestConfiguration; +import org.mycore.common.MCRTestProperty; +import org.mycore.common.config.annotation.MCRClassProperty; +import org.mycore.common.config.annotation.MCRClassPropertyList; +import org.mycore.common.config.annotation.MCRClassPropertyMap; +import org.mycore.common.config.instantiator.MCRInstanceConfiguration; +import org.mycore.test.MyCoReTest; + +@MyCoReTest +public class MCRInstantiatorClassPropertyBasicTest { + + @Test + @MCRTestConfiguration( + properties = { + @MCRTestProperty(key = "Foo.Class", classNameOf = TestClass.class), + @MCRTestProperty(key = "Foo.Value", classNameOf = FooValue.class), + }) + public void classValue() { + + TestClass instance = ofName(TestClass.class, "Foo").instantiate(); + + assertNotNull(instance); + assertEquals(FooValue.class, instance.value); + + } + + @Test + @MCRTestConfiguration( + properties = { + @MCRTestProperty(key = "Foo.Class", classNameOf = TestClass.class), + @MCRTestProperty(key = "Foo.Value", classNameOf = IncompatibleValue.class), + }) + public void incompatibleClassValue() { + + MCRConfigurationException exception = assertThrows(MCRConfigurationException.class, + () -> MCRInstanceConfiguration.ofName(TestClass.class, "Foo").instantiate()); + + assertEquals("Class, configured in Foo.Value (and sub-properties thereof)," + + " for target field 'value' in configured class " + TestClass.class.getName() + + " has a class (" + IncompatibleValue.class.getName() + ") that is incompatible" + + " with the annotated class (" + Value.class.getName() + ")", exception.getMessage()); + + } + + @Test + @MCRTestConfiguration( + properties = { + @MCRTestProperty(key = "Foo.Class", classNameOf = TestClassWithMap.class), + @MCRTestProperty(key = "Foo.Values.foo", classNameOf = FooValue.class), + @MCRTestProperty(key = "Foo.Values.foo.Extra", string = "FOO"), + @MCRTestProperty(key = "Foo.Values.bar", classNameOf = BarValue.class), + @MCRTestProperty(key = "Foo.Values.bar.Extra", string = "BAR"), + }) + public void classValueMap() { + + TestClassWithMap instance = ofName(TestClassWithMap.class, "Foo").instantiate(); + + assertNotNull(instance); + assertEquals(FooValue.class, instance.values.get("foo")); + assertEquals(BarValue.class, instance.values.get("bar")); + + } + + @Test + @MCRTestConfiguration( + properties = { + @MCRTestProperty(key = "Foo.Class", classNameOf = TestClassWithList.class), + @MCRTestProperty(key = "Foo.Values.10", classNameOf = FooValue.class), + @MCRTestProperty(key = "Foo.Values.10.Extra", string = "FOO"), + @MCRTestProperty(key = "Foo.Values.20", classNameOf = BarValue.class), + @MCRTestProperty(key = "Foo.Values.20.Extra", string = "BAR"), + }) + public void classValueList() { + + TestClassWithList instance = ofName(TestClassWithList.class, "Foo").instantiate(); + + assertNotNull(instance); + assertEquals(FooValue.class, instance.values.getFirst()); + assertEquals(BarValue.class, instance.values.getLast()); + + } + + public static class TestClass { + + @MCRClassProperty(name = "Value", valueClass = Value.class) + public Class value; + + } + + public static class TestClassWithMap { + + @MCRClassPropertyMap(name = "Values", valueClass = Value.class) + public Map> values; + + } + + public static class TestClassWithList { + + @MCRClassPropertyList(name = "Values", valueClass = Value.class) + public List> values; + + } + + public interface Value { + + } + + public static final class FooValue implements Value { + + } + + public static final class BarValue implements Value { + + } + + public static final class IncompatibleValue { + + } + +} diff --git a/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorClassPropertyListTest.java b/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorClassPropertyListTest.java new file mode 100644 index 0000000000..964f356da8 --- /dev/null +++ b/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorClassPropertyListTest.java @@ -0,0 +1,394 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.common.config; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mycore.common.MCRTestConfiguration; +import org.mycore.common.MCRTestProperty; +import org.mycore.common.config.annotation.MCRClassPropertyList; +import org.mycore.common.config.instantiator.MCRInstanceConfiguration; +import org.mycore.test.MyCoReTest; + +/** + * Tests for the following conditions: + *
    + *
  1. Annotation has required = false or not
  2. + *
  3. Property value (for a single-element list) is not set, set empty in short form, + * set non-empty in short form or set non-empty in long form
  4. + *
  5. Annotation has defaultName = "..." or not
  6. + *
  7. Default property value (for a single-element map) is not set, set empty in short form, + * set non-empty in short form or set non-empty in long form
  8. + *
+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Expected results for different conditions
PropertyHas DefaultDefault PropertyExpected Optional ResultExpected Required Result
not set / A=no-[]Exception
not set / A=yesnot set / X=[]Exception
not set / A=yesX=y,z[y, z][y, z]
not set / A=yesX.1=y, X.2=z[y, z][y, z]
A=b,c--[b, c][b, c]
A.1=b, A.2=c--[b, c][b, c]
+ */ +@MyCoReTest +public class MCRInstantiatorClassPropertyListTest { + + private static final Logger LOGGER = LogManager.getLogger(); + + public static final String CONFIGURED_CLASS_PROPERTY = "Foo.Class"; + + public static final String LIST_PROPERTY = "Foo.List"; + + public static final String LIST_PROPERTY_1 = "Foo.List.1"; + + public static final String LIST_PROPERTY_2 = "Foo.List.2"; + + public static final String DEFAULT_LIST_PROPERTY = "MCR.List"; + + public static final String DEFAULT_LIST_PROPERTY_1 = "MCR.List.1"; + + public static final String DEFAULT_LIST_PROPERTY_2 = "MCR.List.2"; + + private static Stream provideAllParameterCombinations() { + List argumentsList = new ArrayList<>(); + for (Boolean required : List.of(false, true)) { + for (ValueProperty valueProperty : ValueProperty.values()) { + for (Boolean defaultValue : List.of(false, true)) { + for (DefaultProperty defaultProperty : DefaultProperty.values()) { + argumentsList.add(Arguments.of(required, valueProperty, defaultValue, defaultProperty)); + } + } + } + } + return argumentsList.stream(); + } + + @ParameterizedTest + @MethodSource("provideAllParameterCombinations") + @MCRTestConfiguration(properties = { + @MCRTestProperty(key = LIST_PROPERTY, empty = true), + @MCRTestProperty(key = LIST_PROPERTY_1, empty = true), + @MCRTestProperty(key = LIST_PROPERTY_2, empty = true), + @MCRTestProperty(key = DEFAULT_LIST_PROPERTY, empty = true), + @MCRTestProperty(key = DEFAULT_LIST_PROPERTY_1, empty = true), + @MCRTestProperty(key = DEFAULT_LIST_PROPERTY_2, empty = true) + }) + void test(boolean required, ValueProperty valueProperty, boolean defaultValue, DefaultProperty defaultProperty) { + + // log all parameters + LOGGER.info("TEST PARAMETERS"); + LOGGER.info("required={}", required); + LOGGER.info("valueProperty={}", valueProperty); + LOGGER.info("defaultValue={}", defaultValue); + LOGGER.info("defaultProperty={}", defaultProperty); + + // select class to be configured + Class configuredClass; + if (required) { + if (defaultValue) { + configuredClass = RequiredDefaultSet.class; + } else { + configuredClass = RequiredDefaultNotSet.class; + } + } else { + if (defaultValue) { + configuredClass = NotRequiredDefaultSet.class; + } else { + configuredClass = NotRequiredDefaultNotSet.class; + } + } + + // set class property for configured instance + MCRConfiguration2.set(CONFIGURED_CLASS_PROPERTY, configuredClass.getName()); + + // set class property for nested instance + // empty strings are default via @MCRTestProperty above, may need to overwrite + switch (valueProperty) { + case NOT_SET -> { + MCRConfiguration2.set(LIST_PROPERTY, (String) null); + MCRConfiguration2.set(LIST_PROPERTY_1, (String) null); + MCRConfiguration2.set(LIST_PROPERTY_2, (String) null); + } + case SET_EMPTY -> { + MCRConfiguration2.set(LIST_PROPERTY, ""); + MCRConfiguration2.set(LIST_PROPERTY_1, (String) null); + MCRConfiguration2.set(LIST_PROPERTY_2, (String) null); + } + case SET_SHORT_FORM -> { + MCRConfiguration2.set(LIST_PROPERTY, Value.class.getName() + ","); + MCRConfiguration2.set(LIST_PROPERTY_1, (String) null); + MCRConfiguration2.set(LIST_PROPERTY_2, (String) null); + } + case SET_LONG_FORM -> { + MCRConfiguration2.set(LIST_PROPERTY, (String) null); + MCRConfiguration2.set(LIST_PROPERTY_1, Value.class.getName()); + } + } + + // set value property for property of nested instance + // empty string is default via @MCRTestProperty above, may need to overwrite + switch (defaultProperty) { + case NOT_SET -> { + MCRConfiguration2.set(DEFAULT_LIST_PROPERTY, (String) null); + MCRConfiguration2.set(DEFAULT_LIST_PROPERTY_1, (String) null); + MCRConfiguration2.set(DEFAULT_LIST_PROPERTY_2, (String) null); + } + case SET_EMPTY -> { + MCRConfiguration2.set(DEFAULT_LIST_PROPERTY, ""); + MCRConfiguration2.set(DEFAULT_LIST_PROPERTY_1, (String) null); + MCRConfiguration2.set(DEFAULT_LIST_PROPERTY_2, (String) null); + } + case SET_SHORT_FORM -> { + MCRConfiguration2.set(DEFAULT_LIST_PROPERTY, DefaultValue.class.getName() + ","); + MCRConfiguration2.set(DEFAULT_LIST_PROPERTY_1, (String) null); + MCRConfiguration2.set(DEFAULT_LIST_PROPERTY_2, (String) null); + } + case SET_LONG_FORM -> { + MCRConfiguration2.set(DEFAULT_LIST_PROPERTY, (String) null); + MCRConfiguration2.set(DEFAULT_LIST_PROPERTY_1, DefaultValue.class.getName()); + } + } + + // log all relevant configuration entries + LOGGER.info("CONFIGURATION PROPERTIES"); + Map propertiesMap = MCRConfiguration2.getRawProperties(); + LOGGER.info("{}={}", CONFIGURED_CLASS_PROPERTY, get(propertiesMap, CONFIGURED_CLASS_PROPERTY)); + LOGGER.info("{}={}", LIST_PROPERTY, get(propertiesMap, LIST_PROPERTY)); + LOGGER.info("{}={}", LIST_PROPERTY_1, get(propertiesMap, LIST_PROPERTY_1)); + LOGGER.info("{}={}", LIST_PROPERTY_2, get(propertiesMap, LIST_PROPERTY_2)); + LOGGER.info("{}={}", DEFAULT_LIST_PROPERTY, get(propertiesMap, DEFAULT_LIST_PROPERTY)); + LOGGER.info("{}={}", DEFAULT_LIST_PROPERTY_1, get(propertiesMap, DEFAULT_LIST_PROPERTY_1)); + LOGGER.info("{}={}", DEFAULT_LIST_PROPERTY_2, get(propertiesMap, DEFAULT_LIST_PROPERTY_2)); + + // perform instantiation of configured class + Configurable instance = null; + MCRConfigurationException exception = null; + try { + instance = MCRInstanceConfiguration.ofName(Configurable.class, "Foo").instantiate(); + } catch (MCRConfigurationException e) { + exception = e; + } + + boolean missingDefault = required && !valueProperty.set() && defaultValue && !defaultProperty.set(); + boolean emptyResultExpected = !valueProperty.set() && (!defaultValue || !defaultProperty.set()); + + if (missingDefault) { + + assertNull(instance); + assertNotNull(exception); + + assertEquals("Default class list, configured in MCR.List (and sub-properties thereof)," + + " for target field 'list' in configured class " + configuredClass.getName() + + " is empty", exception.getMessage()); + + } else if (required && emptyResultExpected) { + + assertNull(instance); + assertNotNull(exception); + + assertEquals("Class list, configured in Foo.List (and sub-properties thereof)," + + " for target field 'list' in configured class " + configuredClass.getName() + + " is empty", exception.getMessage()); + + } else { + + assertNull(exception); + assertNotNull(instance); + + List> list = instance.list(); + assertNotNull(list); + + if (emptyResultExpected) { + + assertTrue(list.isEmpty()); + + } else { + + assertEquals(1, list.size()); + Class value = list.getFirst(); + assertNotNull(value); + + if (valueProperty.set()) { + assertEquals(Value.class, value); + } else { + assertEquals(DefaultValue.class, value); + } + + } + + } + + } + + private static String get(Map map, String key) { + String value = map.get(key); + return value == null ? "null" : "'" + value + "'"; + } + + enum ValueProperty { + + NOT_SET, + + SET_EMPTY, + + SET_SHORT_FORM, + + SET_LONG_FORM; + + public boolean set() { + return this == SET_SHORT_FORM || this == SET_LONG_FORM; + } + + } + + enum DefaultProperty { + + NOT_SET, + + SET_EMPTY, + + SET_SHORT_FORM, + + SET_LONG_FORM; + + public boolean set() { + return this == SET_SHORT_FORM || this == SET_LONG_FORM; + } + + } + + public interface Configurable { + + List> list(); + + } + + public static class NotRequiredDefaultNotSet implements Configurable { + + @MCRClassPropertyList(name = "List", required = false) + public List> list; + + @Override + public List> list() { + return list; + } + + } + + public static class NotRequiredDefaultSet implements Configurable { + + @MCRClassPropertyList(name = "List", required = false, defaultName = "MCR.List") + public List> list; + + @Override + public List> list() { + return list; + } + + } + + public static class RequiredDefaultNotSet implements Configurable { + + @MCRClassPropertyList(name = "List") + public List> list; + + @Override + public List> list() { + return list; + } + + } + + public static class RequiredDefaultSet implements Configurable { + + @MCRClassPropertyList(name = "List", defaultName = "MCR.List") + public List> list; + + @Override + public List> list() { + return list; + } + + } + + public static final class Value { + + } + + public static final class DefaultValue { + + } + +} diff --git a/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorClassPropertyMapTest.java b/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorClassPropertyMapTest.java new file mode 100644 index 0000000000..fc345dcbdf --- /dev/null +++ b/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorClassPropertyMapTest.java @@ -0,0 +1,397 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.common.config; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mycore.common.MCRTestConfiguration; +import org.mycore.common.MCRTestProperty; +import org.mycore.common.config.annotation.MCRClassPropertyMap; +import org.mycore.common.config.instantiator.MCRInstanceConfiguration; +import org.mycore.test.MyCoReTest; + +/** + * Tests for the following conditions: + *
    + *
  1. Annotation has required = false or not
  2. + *
  3. Property value (for a single-element map) is not set, set empty in short form, + * set non-empty in short form or set non-empty in long form
  4. + *
  5. Annotation has defaultName = "..." or not
  6. + *
  7. Default property value (for a single-element map) is not set, set empty in short form, + * set non-empty in short form or set non-empty in long form
  8. + *
+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Expected results for different conditions
PropertyHas DefaultDefault PropertyExpected Optional ResultExpected Required Result
not set / A=no-{}Exception
not set / A=yesnot set / X={}Exception
not set / A=yesX=Y:y,Z:z{Y=y, Z=z}{Y=y, Z=z}
not set / A=yesX.Y=y, X.Z=z{Y=y, Z=z}{Y=y, Z=z}
A=B:b,C:c--{B=b, C=c}{B=b, C=c}
A.B=b, A.C=c--{B=b, C=c}{B=b, C=c}
+ */ +@MyCoReTest +public class MCRInstantiatorClassPropertyMapTest { + + private static final Logger LOGGER = LogManager.getLogger(); + + public static final String CONFIGURED_CLASS_PROPERTY = "Foo.Class"; + + public static final String MAP_PROPERTY = "Foo.Map"; + + public static final String MAP_PROPERTY_NON_EMPTY = "Foo.Map.nonEmpty"; + + public static final String MAP_PROPERTY_EMPTY = "Foo.Map.empty"; + + public static final String DEFAULT_MAP_PROPERTY = "MCR.Map"; + + public static final String DEFAULT_MAP_PROPERTY_NON_EMPTY = "MCR.Map.nonEmpty"; + + public static final String DEFAULT_MAP_PROPERTY_EMPTY = "MCR.Map.empty"; + + private static Stream provideAllParameterCombinations() { + List argumentsList = new ArrayList<>(); + for (Boolean required : List.of(false, true)) { + for (ValueProperty valueProperty : ValueProperty + .values()) { + for (Boolean defaultValue : List.of(false, true)) { + for (DefaultProperty defaultProperty : DefaultProperty + .values()) { + argumentsList.add(Arguments.of(required, valueProperty, defaultValue, defaultProperty)); + } + } + } + } + return argumentsList.stream(); + } + + @ParameterizedTest + @MethodSource("provideAllParameterCombinations") + @MCRTestConfiguration(properties = { + @MCRTestProperty(key = MAP_PROPERTY, empty = true), + @MCRTestProperty(key = MAP_PROPERTY_NON_EMPTY, empty = true), + @MCRTestProperty(key = MAP_PROPERTY_EMPTY, empty = true), + @MCRTestProperty(key = DEFAULT_MAP_PROPERTY, empty = true), + @MCRTestProperty(key = DEFAULT_MAP_PROPERTY_NON_EMPTY, empty = true), + @MCRTestProperty(key = DEFAULT_MAP_PROPERTY_EMPTY, empty = true) + }) + void test(boolean required, ValueProperty valueProperty, + boolean defaultValue, DefaultProperty defaultProperty) { + + // log all parameters + LOGGER.info("TEST PARAMETERS"); + LOGGER.info("required={}", required); + LOGGER.info("valueProperty={}", valueProperty); + LOGGER.info("defaultValue={}", defaultValue); + LOGGER.info("defaultProperty={}", defaultProperty); + + // select class to be configured + Class configuredClass; + if (required) { + if (defaultValue) { + configuredClass = RequiredDefaultSet.class; + } else { + configuredClass = RequiredDefaultNotSet.class; + } + } else { + if (defaultValue) { + configuredClass = NotRequiredDefaultSet.class; + } else { + configuredClass = NotRequiredDefaultNotSet.class; + } + } + + // set class property for configured instance + MCRConfiguration2.set(CONFIGURED_CLASS_PROPERTY, configuredClass.getName()); + + // set class property for nested instance + // empty strings are default via @MCRTestProperty above, may need to overwrite + switch (valueProperty) { + case NOT_SET -> { + MCRConfiguration2.set(MAP_PROPERTY, (String) null); + MCRConfiguration2.set(MAP_PROPERTY_NON_EMPTY, (String) null); + MCRConfiguration2.set(MAP_PROPERTY_EMPTY, (String) null); + } + case SET_EMPTY -> { + MCRConfiguration2.set(MAP_PROPERTY, ""); + MCRConfiguration2.set(MAP_PROPERTY_NON_EMPTY, (String) null); + MCRConfiguration2.set(MAP_PROPERTY_EMPTY, (String) null); + } + case SET_SHORT_FORM -> { + MCRConfiguration2.set(MAP_PROPERTY, "nonEmpty:" + Value.class.getName() + ",empty:"); + MCRConfiguration2.set(MAP_PROPERTY_NON_EMPTY, (String) null); + MCRConfiguration2.set(MAP_PROPERTY_EMPTY, (String) null); + } + case SET_LONG_FORM -> { + MCRConfiguration2.set(MAP_PROPERTY, (String) null); + MCRConfiguration2.set(MAP_PROPERTY_NON_EMPTY, Value.class.getName()); + } + } + + // set value property for property of nested instance + // empty string is default via @MCRTestProperty above, may need to overwrite + switch (defaultProperty) { + case NOT_SET -> { + MCRConfiguration2.set(DEFAULT_MAP_PROPERTY, (String) null); + MCRConfiguration2.set(DEFAULT_MAP_PROPERTY_NON_EMPTY, (String) null); + MCRConfiguration2.set(DEFAULT_MAP_PROPERTY_EMPTY, (String) null); + } + case SET_EMPTY -> { + MCRConfiguration2.set(DEFAULT_MAP_PROPERTY, ""); + MCRConfiguration2.set(DEFAULT_MAP_PROPERTY_NON_EMPTY, (String) null); + MCRConfiguration2.set(DEFAULT_MAP_PROPERTY_EMPTY, (String) null); + } + case SET_SHORT_FORM -> { + MCRConfiguration2.set(DEFAULT_MAP_PROPERTY, "nonEmpty:" + DefaultValue.class.getName() + ",empty:"); + MCRConfiguration2.set(DEFAULT_MAP_PROPERTY_NON_EMPTY, (String) null); + MCRConfiguration2.set(DEFAULT_MAP_PROPERTY_EMPTY, (String) null); + } + case SET_LONG_FORM -> { + MCRConfiguration2.set(DEFAULT_MAP_PROPERTY, (String) null); + MCRConfiguration2.set(DEFAULT_MAP_PROPERTY_NON_EMPTY, DefaultValue.class.getName()); + } + } + + // log all relevant configuration entries + LOGGER.info("CONFIGURATION PROPERTIES"); + Map propertiesMap = MCRConfiguration2.getRawProperties(); + LOGGER.info("{}={}", CONFIGURED_CLASS_PROPERTY, get(propertiesMap, CONFIGURED_CLASS_PROPERTY)); + LOGGER.info("{}={}", MAP_PROPERTY, get(propertiesMap, MAP_PROPERTY)); + LOGGER.info("{}={}", MAP_PROPERTY_NON_EMPTY, get(propertiesMap, MAP_PROPERTY_NON_EMPTY)); + LOGGER.info("{}={}", MAP_PROPERTY_EMPTY, get(propertiesMap, MAP_PROPERTY_EMPTY)); + LOGGER.info("{}={}", DEFAULT_MAP_PROPERTY, get(propertiesMap, DEFAULT_MAP_PROPERTY)); + LOGGER.info("{}={}", DEFAULT_MAP_PROPERTY_NON_EMPTY, get(propertiesMap, DEFAULT_MAP_PROPERTY_NON_EMPTY)); + LOGGER.info("{}={}", DEFAULT_MAP_PROPERTY_EMPTY, get(propertiesMap, DEFAULT_MAP_PROPERTY_EMPTY)); + + // perform instantiation of configured class + Configurable instance = null; + MCRConfigurationException exception = null; + try { + instance = MCRInstanceConfiguration.ofName(Configurable.class, "Foo").instantiate(); + } catch (MCRConfigurationException e) { + exception = e; + } + + boolean missingDefault = required && !valueProperty.set() && defaultValue && !defaultProperty.set(); + boolean emptyResultExpected = !valueProperty.set() && (!defaultValue || !defaultProperty.set()); + + if (missingDefault) { + + assertNull(instance); + assertNotNull(exception); + + assertEquals("Default class map, configured in MCR.Map (and sub-properties thereof)," + + " for target field 'map' in configured class " + configuredClass.getName() + + " is empty", exception.getMessage()); + + } else if (required && emptyResultExpected) { + + assertNull(instance); + assertNotNull(exception); + + assertEquals("Class map, configured in Foo.Map (and sub-properties thereof)," + + " for target field 'map' in configured class " + configuredClass.getName() + + " is empty", exception.getMessage()); + + } else { + + assertNull(exception); + assertNotNull(instance); + + Map> list = instance.map(); + assertNotNull(list); + + if (emptyResultExpected) { + + assertTrue(list.isEmpty()); + + } else { + + assertEquals(1, list.size()); + Class value = list.get("nonEmpty"); + assertNotNull(value); + + if (valueProperty.set()) { + assertEquals(Value.class, value); + } else { + assertEquals(DefaultValue.class, value); + } + + } + + } + + } + + private static String get(Map map, String key) { + String value = map.get(key); + return value == null ? "null" : "'" + value + "'"; + } + + enum ValueProperty { + + NOT_SET, + + SET_EMPTY, + + SET_SHORT_FORM, + + SET_LONG_FORM; + + public boolean set() { + return this == SET_SHORT_FORM || this == SET_LONG_FORM; + } + + } + + enum DefaultProperty { + + NOT_SET, + + SET_EMPTY, + + SET_SHORT_FORM, + + SET_LONG_FORM; + + public boolean set() { + return this == SET_SHORT_FORM || this == SET_LONG_FORM; + } + + } + + public interface Configurable { + + Map> map(); + + } + + public static class NotRequiredDefaultNotSet implements Configurable { + + @MCRClassPropertyMap(name = "Map", required = false) + public Map> map; + + @Override + public Map> map() { + return map; + } + + } + + public static class NotRequiredDefaultSet implements Configurable { + + @MCRClassPropertyMap(name = "Map", required = false, defaultName = "MCR.Map") + public Map> map; + + @Override + public Map> map() { + return map; + } + + } + + public static class RequiredDefaultNotSet implements Configurable { + + @MCRClassPropertyMap(name = "Map") + public Map> map; + + @Override + public Map> map() { + return map; + } + + } + + public static class RequiredDefaultSet implements Configurable { + + @MCRClassPropertyMap(name = "Map", defaultName = "MCR.Map") + public Map> map; + + @Override + public Map> map() { + return map; + } + + } + + public static final class Value { + + } + + public static final class DefaultValue { + + } + +} diff --git a/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorClassPropertyTest.java b/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorClassPropertyTest.java new file mode 100644 index 0000000000..3e3a995ae2 --- /dev/null +++ b/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorClassPropertyTest.java @@ -0,0 +1,325 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.common.config; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mycore.common.MCRTestConfiguration; +import org.mycore.common.MCRTestProperty; +import org.mycore.common.config.annotation.MCRClassProperty; +import org.mycore.common.config.instantiator.MCRInstanceConfiguration; +import org.mycore.test.MyCoReTest; + +/** + * Tests for the following conditions: + *
    + *
  1. Annotation has required = false or not
  2. + *
  3. Property value is not set, empty or non-empty
  4. + *
  5. Annotation has defaultName = "..." or not
  6. + *
  7. Default property value is not set, empty or non-empty
  8. + *
+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Expected results for different conditions
PropertyHas DefaultDefault PropertyExpected Optional ResultExpected Required Result
not set / emptyno-nullException
not set / emptyyesnot set / emptyExceptionException
not set / emptyyesDefaultValueDefaultValueDefaultValue
Value--ValueValue
+ */ +@MyCoReTest +public class MCRInstantiatorClassPropertyTest { + + private static final Logger LOGGER = LogManager.getLogger(); + + public static final String CONFIGURED_CLASS_PROPERTY = "Foo.Class"; + + public static final String VALUE_PROPERTY = "Foo.Value"; + + public static final String DEFAULT_VALUE_PROPERTY = "MCR.Value"; + + private static Stream provideAllParameterCombinations() { + List argumentsList = new ArrayList<>(); + for (Boolean required : List.of(false, true)) { + for (ValueProperty valueProperty : ValueProperty + .values()) { + for (Boolean defaultValue : List.of(false, true)) { + for (DefaultProperty defaultProperty : DefaultProperty + .values()) { + argumentsList.add(Arguments.of(required, valueProperty, defaultValue, defaultProperty)); + } + } + } + } + return argumentsList.stream(); + } + + @ParameterizedTest + @MethodSource("provideAllParameterCombinations") + @MCRTestConfiguration(properties = { + @MCRTestProperty(key = VALUE_PROPERTY, empty = true), + @MCRTestProperty(key = DEFAULT_VALUE_PROPERTY, empty = true) + }) + void test(boolean required, ValueProperty valueProperty, + boolean defaultValue, DefaultProperty defaultProperty) { + + // log all parameters + LOGGER.info("TEST PARAMETERS"); + LOGGER.info("required={}", required); + LOGGER.info("valueProperty={}", valueProperty); + LOGGER.info("defaultValue={}", defaultValue); + LOGGER.info("defaultProperty={}", defaultProperty); + + // select class to be configured + Class configuredClass; + if (required) { + if (defaultValue) { + configuredClass = RequiredDefaultSet.class; + } else { + configuredClass = RequiredDefaultNotSet.class; + } + } else { + if (defaultValue) { + configuredClass = NotRequiredDefaultSet.class; + } else { + configuredClass = NotRequiredDefaultNotSet.class; + } + } + + // set class property for configured instance + MCRConfiguration2.set(CONFIGURED_CLASS_PROPERTY, configuredClass.getName()); + + // set class property for nested instance + // empty strings are default via @MCRTestProperty above, may need to overwrite + switch (valueProperty) { + case NOT_SET -> MCRConfiguration2.set(VALUE_PROPERTY, (String) null); + case SET_EMPTY -> MCRConfiguration2.set(VALUE_PROPERTY, ""); + case SET_NON_EMPTY -> MCRConfiguration2.set(VALUE_PROPERTY, Value.class.getName()); + } + + // set value property for property of nested instance + // empty string is default via @MCRTestProperty above, may need to overwrite + switch (defaultProperty) { + case NOT_SET -> MCRConfiguration2.set(DEFAULT_VALUE_PROPERTY, (String) null); + case SET_EMPTY -> MCRConfiguration2.set(DEFAULT_VALUE_PROPERTY, ""); + case SET_NON_EMPTY -> MCRConfiguration2.set(DEFAULT_VALUE_PROPERTY, DefaultValue.class.getName()); + } + + // log all relevant configuration entries + LOGGER.info("CONFIGURATION PROPERTIES"); + Map propertiesMap = MCRConfiguration2.getRawProperties(); + LOGGER.info("{}={}", CONFIGURED_CLASS_PROPERTY, get(propertiesMap, CONFIGURED_CLASS_PROPERTY)); + LOGGER.info("{}={}", VALUE_PROPERTY, get(propertiesMap, VALUE_PROPERTY)); + LOGGER.info("{}={}", DEFAULT_VALUE_PROPERTY, get(propertiesMap, DEFAULT_VALUE_PROPERTY)); + + // perform instantiation of configured class + Configurable instance = null; + MCRConfigurationException exception = null; + try { + instance = MCRInstanceConfiguration.ofName(Configurable.class, "Foo").instantiate(); + } catch (MCRConfigurationException e) { + exception = e; + } + + boolean missingDefault = required && !valueProperty.set() && defaultValue && !defaultProperty.set(); + boolean nullResultExpected = !valueProperty.set() && (!defaultValue || !defaultProperty.set()); + + if (missingDefault) { + + assertNull(instance); + assertNotNull(exception); + + assertEquals("Default class, configured in MCR.Value (and sub-properties thereof)," + + " for target field 'value' in configured class " + configuredClass.getName() + + " is missing", exception.getMessage()); + + } else if (required && nullResultExpected) { + + assertNull(instance); + assertNotNull(exception); + + assertEquals("Class, configured in Foo.Value (and sub-properties thereof)," + + " for target field 'value' in configured class " + configuredClass.getName() + + " is missing", exception.getMessage()); + + } else { + + assertNull(exception); + assertNotNull(instance); + + Class value = instance.value(); + + if (nullResultExpected) { + + assertNull(value); + + } else { + + assertNotNull(value); + + if (valueProperty.set()) { + assertEquals(Value.class, value); + } else { + assertEquals(DefaultValue.class, value); + } + + } + + } + + } + + private static String get(Map map, String key) { + String value = map.get(key); + return value == null ? "null" : "'" + value + "'"; + } + + enum ValueProperty { + + NOT_SET, + + SET_EMPTY, + + SET_NON_EMPTY; + + public boolean set() { + return this == SET_NON_EMPTY; + } + + } + + enum DefaultProperty { + + NOT_SET, + + SET_EMPTY, + + SET_NON_EMPTY; + + public boolean set() { + return this == SET_NON_EMPTY; + } + + } + + public interface Configurable { + + Class value(); + + } + + public static class NotRequiredDefaultNotSet implements Configurable { + + @MCRClassProperty(name = "Value", required = false) + public Class value; + + @Override + public Class value() { + return value; + } + + } + + public static class NotRequiredDefaultSet implements Configurable { + + @MCRClassProperty(name = "Value", required = false, defaultName = "MCR.Value") + public Class value; + + @Override + public Class value() { + return value; + } + + } + + public static class RequiredDefaultNotSet implements Configurable { + + @MCRClassProperty(name = "Value") + public Class value; + + @Override + public Class value() { + return value; + } + + } + + public static class RequiredDefaultSet implements Configurable { + + @MCRClassProperty(name = "Value", defaultName = "MCR.Value") + public Class value; + + @Override + public Class value() { + return value; + } + + } + + public static final class Value { + + } + + public static final class DefaultValue { + + } + +} diff --git a/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorImplicitListTest.java b/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorImplicitListTest.java index 3639de9591..305b5b8a0a 100644 --- a/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorImplicitListTest.java +++ b/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorImplicitListTest.java @@ -77,49 +77,28 @@ * Exception * * - * set empty - * - - * - - * [] - * Exception - * - * - * not set + * not set / empty * none or impl. enabled - * not set + * not set / 1.a= * [] * Exception * * - * not set - * none or impl. enabled - * 1.a= - * [()] - * [()] - * - * - * not set + * not set / empty * none or impl. enabled * 1.a=Value * [(Value)] * [(Value)] * * - * not set + * not set / empty * expl. enabled - * not set + * not set / 1.a= * [_] * [_] * * - * not set - * expl. enabled - * 1.a= - * [()] - * [()] - * - * - * not set + * not set / empty * expl. enabled * 1.a=Value * [(Value)] @@ -128,20 +107,13 @@ * * set * - - * not set + * not set / 1.a= * [_] * [_] * * * set * - - * 1.a= - * [()] - * [()] - * - * - * set - * - * 1.a=Value * [(Value)] * [(Value)] @@ -231,7 +203,7 @@ void test(boolean required, ClassProperty classProperty, Sentinel sentinel, Valu // log all relevant configuration entries LOGGER.info("CONFIGURATION PROPERTIES"); - Map propertiesMap = MCRConfiguration2.getPropertiesMap(); + Map propertiesMap = MCRConfiguration2.getRawProperties(); LOGGER.info("{}={}", CONFIGURED_CLASS_PROPERTY, get(propertiesMap, CONFIGURED_CLASS_PROPERTY)); LOGGER.info("{}={}", NESTED_CLASS_PROPERTY, get(propertiesMap, NESTED_CLASS_PROPERTY)); LOGGER.info("{}={}", NESTED_SENTINEL_PROPERTY, get(propertiesMap, NESTED_SENTINEL_PROPERTY)); @@ -248,14 +220,13 @@ void test(boolean required, ClassProperty classProperty, Sentinel sentinel, Valu // all the indications a nested class should be instantiated (implicitly) boolean shouldInstantiateNestedClass = false; - shouldInstantiateNestedClass |= classProperty != ClassProperty.NOT_SET; - shouldInstantiateNestedClass |= valueProperty != ValueProperty.NOT_SET; + shouldInstantiateNestedClass |= classProperty.set(); + shouldInstantiateNestedClass |= valueProperty.set(); shouldInstantiateNestedClass |= sentinel == Sentinel.EXPLICITLY_ENABLED; // all the indications a nested class should not be instantiated (or instantiation should be suppressed) boolean shouldNotInstantiateNestedClass = false; shouldNotInstantiateNestedClass |= !shouldInstantiateNestedClass; - shouldNotInstantiateNestedClass |= classProperty == ClassProperty.SET_EMPTY; shouldNotInstantiateNestedClass |= sentinel == Sentinel.IMPLICITLY_DISABLED; shouldNotInstantiateNestedClass |= sentinel == Sentinel.EXPLICITLY_DISABLED; @@ -264,7 +235,7 @@ void test(boolean required, ClassProperty classProperty, Sentinel sentinel, Valu assertNull(instance); assertNotNull(exception); - assertEquals("Instance list, configured in Foo.Nested (and its sub-properties)," + + assertEquals("Instance list, configured in Foo.Nested (and sub-properties thereof)," + " for target field 'nestedList' in configured class " + configuredClass.getName() + " is empty", exception.getMessage()); @@ -287,8 +258,7 @@ void test(boolean required, ClassProperty classProperty, Sentinel sentinel, Valu assertNotNull(nested); switch (valueProperty) { - case NOT_SET -> assertNull(nested.value); - case SET_EMPTY -> assertEquals("", nested.value); + case NOT_SET, SET_EMPTY -> assertNull(nested.value); case SET_NON_EMPTY -> assertEquals("Value", nested.value); } @@ -309,7 +279,11 @@ enum ClassProperty { SET_EMPTY, - SET_NON_EMPTY + SET_NON_EMPTY; + + public boolean set() { + return this == SET_NON_EMPTY; + } } @@ -333,7 +307,11 @@ enum ValueProperty { SET_EMPTY, - SET_NON_EMPTY + SET_NON_EMPTY; + + public boolean set() { + return this == SET_NON_EMPTY; + } } diff --git a/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorImplicitMapTest.java b/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorImplicitMapTest.java index 1bc82197aa..ca7358adff 100644 --- a/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorImplicitMapTest.java +++ b/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorImplicitMapTest.java @@ -77,49 +77,28 @@ * Exception * * - * set empty - * - - * - - * {} - * Exception - * - * - * not set + * not set / empty * none or impl. enabled - * not set + * not set / A.a= * {} * Exception * * - * not set - * none or impl. enabled - * A.a= - * {A=()} - * {A=()} - * - * - * not set + * not set / empty * none or impl. enabled * A.a=Value * {A=(Value)} * {A=(Value)} * * - * not set + * not set / empty * expl. enabled - * not set + * not set / A.a= * {A=_} * {A=_} * * - * not set - * expl. enabled - * A.a= - * {A=()} - * {A=()} - * - * - * not set + * not set / empty * expl. enabled * A.a=Value * {A=(Value)} @@ -128,20 +107,13 @@ * * set * - - * not set + * not set / A.a= * {A=_} * {A=_} * * * set * - - * A.a= - * {A=()} - * {A=()} - * - * - * set - * - * A.a=Value * {A=(Value)} * {A=(Value)} @@ -231,7 +203,7 @@ void test(boolean required, ClassProperty classProperty, Sentinel sentinel, Valu // log all relevant configuration entries LOGGER.info("CONFIGURATION PROPERTIES"); - Map propertiesMap = MCRConfiguration2.getPropertiesMap(); + Map propertiesMap = MCRConfiguration2.getRawProperties(); LOGGER.info("{}={}", CONFIGURED_CLASS_PROPERTY, get(propertiesMap, CONFIGURED_CLASS_PROPERTY)); LOGGER.info("{}={}", NESTED_CLASS_PROPERTY, get(propertiesMap, NESTED_CLASS_PROPERTY)); LOGGER.info("{}={}", NESTED_SENTINEL_PROPERTY, get(propertiesMap, NESTED_SENTINEL_PROPERTY)); @@ -248,14 +220,13 @@ void test(boolean required, ClassProperty classProperty, Sentinel sentinel, Valu // all the indications a nested class should be instantiated (implicitly) boolean shouldInstantiateNestedClass = false; - shouldInstantiateNestedClass |= classProperty != ClassProperty.NOT_SET; - shouldInstantiateNestedClass |= valueProperty != ValueProperty.NOT_SET; + shouldInstantiateNestedClass |= classProperty.set(); + shouldInstantiateNestedClass |= valueProperty.set(); shouldInstantiateNestedClass |= sentinel == Sentinel.EXPLICITLY_ENABLED; // all the indications a nested class should not be instantiated (or instantiation should be suppressed) boolean shouldNotInstantiateNestedClass = false; shouldNotInstantiateNestedClass |= !shouldInstantiateNestedClass; - shouldNotInstantiateNestedClass |= classProperty == ClassProperty.SET_EMPTY; shouldNotInstantiateNestedClass |= sentinel == Sentinel.IMPLICITLY_DISABLED; shouldNotInstantiateNestedClass |= sentinel == Sentinel.EXPLICITLY_DISABLED; @@ -264,7 +235,7 @@ void test(boolean required, ClassProperty classProperty, Sentinel sentinel, Valu assertNull(instance); assertNotNull(exception); - assertEquals("Instance map, configured in Foo.Nested (and its sub-properties)," + + assertEquals("Instance map, configured in Foo.Nested (and sub-properties thereof)," + " for target field 'nestedMap' in configured class " + configuredClass.getName() + " is empty", exception.getMessage()); @@ -287,8 +258,7 @@ void test(boolean required, ClassProperty classProperty, Sentinel sentinel, Valu assertNotNull(nested); switch (valueProperty) { - case NOT_SET -> assertNull(nested.value); - case SET_EMPTY -> assertEquals("", nested.value); + case NOT_SET, SET_EMPTY -> assertNull(nested.value); case SET_NON_EMPTY -> assertEquals("Value", nested.value); } @@ -309,7 +279,11 @@ enum ClassProperty { SET_EMPTY, - SET_NON_EMPTY + SET_NON_EMPTY; + + public boolean set() { + return this == SET_NON_EMPTY; + } } @@ -333,7 +307,11 @@ enum ValueProperty { SET_EMPTY, - SET_NON_EMPTY + SET_NON_EMPTY; + + public boolean set() { + return this == SET_NON_EMPTY; + } } diff --git a/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorImplicitTest.java b/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorImplicitTest.java index 9d1c000341..2201ea5e30 100644 --- a/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorImplicitTest.java +++ b/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorImplicitTest.java @@ -76,49 +76,28 @@ * Exception * * - * set empty - * - - * - - * null - * Exception - * - * - * not set + * not set / empty * none or impl. enabled - * not set + * not set / a= * _ * _ * * - * not set - * none or impl. enabled - * a= - * () - * () - * - * - * not set + * not set / empty * none or impl. enabled * a=Value * (Value) * (Value) * * - * not set + * not set / empty * expl. enabled - * not set + * not set / a= * _ * _ * * - * not set - * expl. enabled - * a= - * () - * () - * - * - * not set + * not set / empty * expl. enabled * a=Value * (Value) @@ -127,20 +106,13 @@ * * set * - - * not set + * not set / a= * _ * _ * * * set * - - * a= - * () - * () - * - * - * set - * - * a=Value * (Value) * (Value) @@ -230,7 +202,7 @@ void test(boolean required, ClassProperty classProperty, Sentinel sentinel, Valu // log all relevant configuration entries LOGGER.info("CONFIGURATION PROPERTIES"); - Map propertiesMap = MCRConfiguration2.getPropertiesMap(); + Map propertiesMap = MCRConfiguration2.getRawProperties(); LOGGER.info("{}={}", CONFIGURED_CLASS_PROPERTY, get(propertiesMap, CONFIGURED_CLASS_PROPERTY)); LOGGER.info("{}={}", NESTED_CLASS_PROPERTY, get(propertiesMap, NESTED_CLASS_PROPERTY)); LOGGER.info("{}={}", NESTED_SENTINEL_PROPERTY, get(propertiesMap, NESTED_SENTINEL_PROPERTY)); @@ -247,8 +219,8 @@ void test(boolean required, ClassProperty classProperty, Sentinel sentinel, Valu // all the indications a nested class should be instantiated (implicitly) boolean shouldInstantiateNestedClass = false; - shouldInstantiateNestedClass |= classProperty != ClassProperty.NOT_SET; - shouldInstantiateNestedClass |= valueProperty != ValueProperty.NOT_SET; + shouldInstantiateNestedClass |= classProperty.set(); + shouldInstantiateNestedClass |= valueProperty.set(); shouldInstantiateNestedClass |= sentinel == Sentinel.NONE; shouldInstantiateNestedClass |= sentinel == Sentinel.IMPLICITLY_ENABLED; shouldInstantiateNestedClass |= sentinel == Sentinel.EXPLICITLY_ENABLED; @@ -256,7 +228,6 @@ void test(boolean required, ClassProperty classProperty, Sentinel sentinel, Valu // all the indications a nested class should not be instantiated (or instantiation should be suppressed) boolean shouldNotInstantiateNestedClass = false; shouldNotInstantiateNestedClass |= !shouldInstantiateNestedClass; - shouldNotInstantiateNestedClass |= classProperty == ClassProperty.SET_EMPTY; shouldNotInstantiateNestedClass |= sentinel == Sentinel.IMPLICITLY_DISABLED; shouldNotInstantiateNestedClass |= sentinel == Sentinel.EXPLICITLY_DISABLED; @@ -265,7 +236,7 @@ void test(boolean required, ClassProperty classProperty, Sentinel sentinel, Valu assertNull(instance); assertNotNull(exception); - assertEquals("Instance, configured in Foo.Nested (and its sub-properties)," + + assertEquals("Instance, configured in Foo.Nested (and sub-properties thereof)," + " for target field 'nested' in configured class " + configuredClass.getName() + " is missing", exception.getMessage()); @@ -285,8 +256,7 @@ void test(boolean required, ClassProperty classProperty, Sentinel sentinel, Valu assertNotNull(nested); switch (valueProperty) { - case NOT_SET -> assertNull(nested.value); - case SET_EMPTY -> assertEquals("", nested.value); + case NOT_SET, SET_EMPTY -> assertNull(nested.value); case SET_NON_EMPTY -> assertEquals("Value", nested.value); } @@ -307,7 +277,11 @@ enum ClassProperty { SET_EMPTY, - SET_NON_EMPTY + SET_NON_EMPTY; + + public boolean set() { + return this == SET_NON_EMPTY; + } } @@ -331,7 +305,11 @@ enum ValueProperty { SET_EMPTY, - SET_NON_EMPTY + SET_NON_EMPTY; + + public boolean set() { + return this == SET_NON_EMPTY; + } } diff --git a/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorInstanceBasicTest.java b/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorInstanceBasicTest.java new file mode 100644 index 0000000000..6105a63d7e --- /dev/null +++ b/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorInstanceBasicTest.java @@ -0,0 +1,149 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.common.config; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mycore.common.config.instantiator.MCRInstanceConfiguration.ofName; + +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.Test; +import org.mycore.common.MCRTestConfiguration; +import org.mycore.common.MCRTestProperty; +import org.mycore.common.config.annotation.MCRInstance; +import org.mycore.common.config.annotation.MCRInstanceList; +import org.mycore.common.config.annotation.MCRInstanceMap; +import org.mycore.common.config.instantiator.MCRInstanceConfiguration; +import org.mycore.test.MyCoReTest; + +@MyCoReTest +public class MCRInstantiatorInstanceBasicTest { + + @Test + @MCRTestConfiguration( + properties = { + @MCRTestProperty(key = "Foo.Class", classNameOf = TestClass.class), + @MCRTestProperty(key = "Foo.Value.Class", classNameOf = FooValue.class), + }) + public void instanceValue() { + + TestClass instance = ofName(TestClass.class, "Foo").instantiate(); + + assertNotNull(instance); + assertEquals(FooValue.class, instance.value.getClass()); + + } + + @Test + @MCRTestConfiguration( + properties = { + @MCRTestProperty(key = "Foo.Class", classNameOf = TestClass.class), + @MCRTestProperty(key = "Foo.Value.Class", classNameOf = IncompatibleValue.class), + }) + public void incompatibleInstanceValue() { + + MCRConfigurationException exception = assertThrows(MCRConfigurationException.class, + () -> MCRInstanceConfiguration.ofName(TestClass.class, "Foo").instantiate()); + + assertEquals("Instance, configured in Foo.Value (and sub-properties thereof)," + + " for target field 'value' in configured class " + TestClass.class.getName() + + " has a class (" + IncompatibleValue.class.getName() + ") that is incompatible" + + " with the annotated class (" + Value.class.getName() + ")", exception.getMessage()); + + } + + @Test + @MCRTestConfiguration( + properties = { + @MCRTestProperty(key = "Foo.Class", classNameOf = TestClassWithMap.class), + @MCRTestProperty(key = "Foo.Values.foo.Class", classNameOf = FooValue.class), + @MCRTestProperty(key = "Foo.Values.foo.Extra", string = "FOO"), + @MCRTestProperty(key = "Foo.Values.bar.Class", classNameOf = BarValue.class), + @MCRTestProperty(key = "Foo.Values.bar.Extra", string = "BAR"), + }) + public void instanceValueMap() { + + TestClassWithMap instance = ofName(TestClassWithMap.class, "Foo").instantiate(); + + assertNotNull(instance); + assertEquals(FooValue.class, instance.values.get("foo").getClass()); + assertEquals(BarValue.class, instance.values.get("bar").getClass()); + + } + + @Test + @MCRTestConfiguration( + properties = { + @MCRTestProperty(key = "Foo.Class", classNameOf = TestClassWithList.class), + @MCRTestProperty(key = "Foo.Values.10.Class", classNameOf = FooValue.class), + @MCRTestProperty(key = "Foo.Values.10.Extra", string = "FOO"), + @MCRTestProperty(key = "Foo.Values.20.Class", classNameOf = BarValue.class), + @MCRTestProperty(key = "Foo.Values.20.Extra", string = "BAR"), + }) + public void instanceValueList() { + + TestClassWithList instance = ofName(TestClassWithList.class, "Foo").instantiate(); + + assertNotNull(instance); + assertEquals(FooValue.class, instance.values.getFirst().getClass()); + assertEquals(BarValue.class, instance.values.getLast().getClass()); + + } + + public static class TestClass { + + @MCRInstance(name = "Value", valueClass = Value.class) + public Value value; + + } + + public static class TestClassWithMap { + + @MCRInstanceMap(name = "Values", valueClass = Value.class) + public Map values; + + } + + public static class TestClassWithList { + + @MCRInstanceList(name = "Values", valueClass = Value.class) + public List values; + + } + + public interface Value { + + } + + public static final class FooValue implements Value { + + } + + public static final class BarValue implements Value { + + } + + public static final class IncompatibleValue { + + } + +} diff --git a/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorNonImplicitListTest.java b/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorNonImplicitListTest.java index 45bc4cbe90..a4e74a6b5f 100644 --- a/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorNonImplicitListTest.java +++ b/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorNonImplicitListTest.java @@ -77,49 +77,28 @@ * Exception * * - * set empty - * - - * - - * [] - * Exception - * - * - * not set - * none or impl. enabled - * not set - * [] - * Exception - * - * - * not set + * not set / empty * none or impl. enabled - * 1.a= + * not set / 1.a= * [] * Exception * * - * not set + * not set / empty * none or impl. enabled * 1.a=Value * [] * Exception * * - * not set - * expl. enabled - * not set - * [] - * Exception - * - * - * not set + * not set / empty * expl. enabled - * 1.a= + * not set / 1.a= * [] * Exception * * - * not set + * not set / empty * expl. enabled * Value * [] @@ -128,20 +107,13 @@ * * set * - - * not set + * not set / 1.a= * [_] * [_] * * * set * - - * 1.a= - * [()] - * [()] - * - * - * set - * - * 1.a=Value * (Value) * [(Value)] @@ -231,7 +203,7 @@ void test(boolean required, ClassProperty classProperty, Sentinel sentinel, Valu // log all relevant configuration entries LOGGER.info("CONFIGURATION PROPERTIES"); - Map propertiesMap = MCRConfiguration2.getPropertiesMap(); + Map propertiesMap = MCRConfiguration2.getRawProperties(); LOGGER.info("{}={}", CONFIGURED_CLASS_PROPERTY, get(propertiesMap, CONFIGURED_CLASS_PROPERTY)); LOGGER.info("{}={}", NESTED_CLASS_PROPERTY, get(propertiesMap, NESTED_CLASS_PROPERTY)); LOGGER.info("{}={}", NESTED_SENTINEL_PROPERTY, get(propertiesMap, NESTED_SENTINEL_PROPERTY)); @@ -258,7 +230,7 @@ void test(boolean required, ClassProperty classProperty, Sentinel sentinel, Valu assertNull(instance); assertNotNull(exception); - assertEquals("Instance list, configured in Foo.Nested (and its sub-properties)," + + assertEquals("Instance list, configured in Foo.Nested (and sub-properties thereof)," + " for target field 'nestedList' in configured class " + configuredClass.getName() + " is empty", exception.getMessage()); @@ -281,8 +253,7 @@ void test(boolean required, ClassProperty classProperty, Sentinel sentinel, Valu assertNotNull(nested); switch (valueProperty) { - case NOT_SET -> assertNull(nested.value); - case SET_EMPTY -> assertEquals("", nested.value); + case NOT_SET, SET_EMPTY -> assertNull(nested.value); case SET_NON_EMPTY -> assertEquals("Value", nested.value); } diff --git a/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorNonImplicitMapTest.java b/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorNonImplicitMapTest.java index 0c6ce84b63..5fd25d5214 100644 --- a/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorNonImplicitMapTest.java +++ b/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorNonImplicitMapTest.java @@ -77,49 +77,28 @@ * Exception * * - * set empty - * - - * - - * {} - * Exception - * - * - * not set - * none or impl. enabled - * not set - * {} - * Exception - * - * - * not set + * not set / empty * none or impl. enabled - * A.a= + * not set / A.a= * {} * Exception * * - * not set + * not set / empty * none or impl. enabled * A.a=Value * {} * Exception * * - * not set - * expl. enabled - * not set - * {} - * Exception - * - * - * not set + * not set / empty * expl. enabled - * A.a= + * not set / A.a= * {} * Exception * * - * not set + * not set / empty * expl. enabled * A=Value * {} @@ -128,20 +107,13 @@ * * set * - - * not set + * not set / A.a= * {A=_} * {A=_} * * * set * - - * A.a= - * {A=()} - * {A=()} - * - * - * set - * - * A.a=Value * A=(Value) * {A=(Value)} @@ -231,7 +203,7 @@ void test(boolean required, ClassProperty classProperty, Sentinel sentinel, Valu // log all relevant configuration entries LOGGER.info("CONFIGURATION PROPERTIES"); - Map propertiesMap = MCRConfiguration2.getPropertiesMap(); + Map propertiesMap = MCRConfiguration2.getRawProperties(); LOGGER.info("{}={}", CONFIGURED_CLASS_PROPERTY, get(propertiesMap, CONFIGURED_CLASS_PROPERTY)); LOGGER.info("{}={}", NESTED_CLASS_PROPERTY, get(propertiesMap, NESTED_CLASS_PROPERTY)); LOGGER.info("{}={}", NESTED_SENTINEL_PROPERTY, get(propertiesMap, NESTED_SENTINEL_PROPERTY)); @@ -258,7 +230,7 @@ void test(boolean required, ClassProperty classProperty, Sentinel sentinel, Valu assertNull(instance); assertNotNull(exception); - assertEquals("Instance map, configured in Foo.Nested (and its sub-properties)," + + assertEquals("Instance map, configured in Foo.Nested (and sub-properties thereof)," + " for target field 'nestedMap' in configured class " + configuredClass.getName() + " is empty", exception.getMessage()); @@ -281,8 +253,7 @@ void test(boolean required, ClassProperty classProperty, Sentinel sentinel, Valu assertNotNull(nested); switch (valueProperty) { - case NOT_SET -> assertNull(nested.value); - case SET_EMPTY -> assertEquals("", nested.value); + case NOT_SET, SET_EMPTY -> assertNull(nested.value); case SET_NON_EMPTY -> assertEquals("Value", nested.value); } diff --git a/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorNonImplicitTest.java b/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorNonImplicitTest.java index 24dffda002..191fe81ebe 100644 --- a/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorNonImplicitTest.java +++ b/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorNonImplicitTest.java @@ -69,13 +69,6 @@ * Expected Required Result * * - * not set - * none or impl. enabled - * not set - * null - * Exception - * - * * - * impl. or expl. disabled * - @@ -83,64 +76,43 @@ * Exception * * - * set empty - * - - * - - * null - * Exception - * - * - * not set + * not set / empty * none or impl. enabled - * a= + * not set / a= * null * Exception * * - * not set + * not set / empty * none or impl. enabled * a=Value * null * Exception * * - * not set + * not set / empty * expl. enabled - * not set + * not set / a= * null * Exception * * - * not set + * not set / empty * expl. enabled - * a= - * null - * Exception - * - * - * not set - * expl. enabled - * Value + * a=Value * null * Exception * * * set * - - * not set + * not set / a= * _ * _ * * * set * - - * a= - * () - * () - * - * - * set - * - * a=Value * (Value) * (Value) @@ -230,7 +202,7 @@ void test(boolean required, ClassProperty classProperty, Sentinel sentinel, Valu // log all relevant configuration entries LOGGER.info("CONFIGURATION PROPERTIES"); - Map propertiesMap = MCRConfiguration2.getPropertiesMap(); + Map propertiesMap = MCRConfiguration2.getRawProperties(); LOGGER.info("{}={}", CONFIGURED_CLASS_PROPERTY, get(propertiesMap, CONFIGURED_CLASS_PROPERTY)); LOGGER.info("{}={}", NESTED_CLASS_PROPERTY, get(propertiesMap, NESTED_CLASS_PROPERTY)); LOGGER.info("{}={}", NESTED_SENTINEL_PROPERTY, get(propertiesMap, NESTED_SENTINEL_PROPERTY)); @@ -257,7 +229,7 @@ void test(boolean required, ClassProperty classProperty, Sentinel sentinel, Valu assertNull(instance); assertNotNull(exception); - assertEquals("Instance, configured in Foo.Nested (and its sub-properties)," + + assertEquals("Instance, configured in Foo.Nested (and sub-properties thereof)," + " for target field 'nested' in configured class " + configuredClass.getName() + " is missing", exception.getMessage()); @@ -277,8 +249,7 @@ void test(boolean required, ClassProperty classProperty, Sentinel sentinel, Valu assertNotNull(nested); switch (valueProperty) { - case NOT_SET -> assertNull(nested.value); - case SET_EMPTY -> assertEquals("", nested.value); + case NOT_SET, SET_EMPTY -> assertNull(nested.value); case SET_NON_EMPTY -> assertEquals("Value", nested.value); } diff --git a/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorPropertyListTest.java b/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorPropertyListTest.java index 259e2cbfe4..c7119896aa 100644 --- a/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorPropertyListTest.java +++ b/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorPropertyListTest.java @@ -59,48 +59,34 @@ * Expected Required Result * * - * not set + * not set / A= * no * - * [] * Exception * * - * not set + * not set / A= * yes - * not set - * Exception - * Exception - * - * - * not set - * yes - * X= + * not set / X= * [] * Exception * * - * not set + * not set / A= * yes * X=y,z * [y, z] * [y, z] * * - * not set + * not set / A= * yes * X.1=y, X.2=z * [y, z] * [y, z] * * - * A= - * - - * - - * [] - * Exception - * - * * A=b,c * - * - @@ -237,7 +223,7 @@ void test(boolean required, ValueProperty valueProperty, boolean defaultValue, D // log all relevant configuration entries LOGGER.info("CONFIGURATION PROPERTIES"); - Map propertiesMap = MCRConfiguration2.getPropertiesMap(); + Map propertiesMap = MCRConfiguration2.getRawProperties(); LOGGER.info("{}={}", CONFIGURED_CLASS_PROPERTY, get(propertiesMap, CONFIGURED_CLASS_PROPERTY)); LOGGER.info("{}={}", LIST_PROPERTY, get(propertiesMap, LIST_PROPERTY)); LOGGER.info("{}={}", LIST_PROPERTY_1, get(propertiesMap, LIST_PROPERTY_1)); @@ -255,31 +241,24 @@ void test(boolean required, ValueProperty valueProperty, boolean defaultValue, D exception = e; } - boolean missingDefaultConfiguration = false; - missingDefaultConfiguration |= valueProperty.notSet() && defaultValue && defaultProperty.notSet(); - missingDefaultConfiguration |= required && valueProperty.notSet() && defaultValue && defaultProperty.setEmpty(); - - // all the indications a nested property should not be created (or creation should be suppressed) - boolean shouldNotCreateProperty = false; - shouldNotCreateProperty |= valueProperty.notSet() && !defaultValue; - shouldNotCreateProperty |= valueProperty.notSet() && defaultValue && defaultProperty.setEmpty(); - shouldNotCreateProperty |= valueProperty.setEmpty(); + boolean missingDefault = required && !valueProperty.set() && defaultValue && !defaultProperty.set(); + boolean emptyResultExpected = !valueProperty.set() && (!defaultValue || !defaultProperty.set()); - if (missingDefaultConfiguration) { + if (missingDefault) { assertNull(instance); assertNotNull(exception); - assertEquals("Default property list, configured in MCR.List (and its sub-properties)," + assertEquals("Default property list, configured in MCR.List (and sub-properties thereof)," + " for target field 'list' in configured class " + configuredClass.getName() + " is empty", exception.getMessage()); - } else if (required && shouldNotCreateProperty) { + } else if (required && emptyResultExpected) { assertNull(instance); assertNotNull(exception); - assertEquals("Property list, configured in Foo.List (and its sub-properties)," + assertEquals("Property list, configured in Foo.List (and sub-properties thereof)," + " for target field 'list' in configured class " + configuredClass.getName() + " is empty", exception.getMessage()); @@ -291,7 +270,7 @@ void test(boolean required, ValueProperty valueProperty, boolean defaultValue, D List list = instance.list(); assertNotNull(list); - if (shouldNotCreateProperty) { + if (emptyResultExpected) { assertTrue(list.isEmpty()); @@ -301,10 +280,10 @@ void test(boolean required, ValueProperty valueProperty, boolean defaultValue, D String value = list.getFirst(); assertNotNull(value); - if (valueProperty.notSet()) { - assertEquals("DefaultValue", value); - } else { + if (valueProperty.set()) { assertEquals("Value", value); + } else { + assertEquals("DefaultValue", value); } } @@ -328,12 +307,8 @@ enum ValueProperty { SET_LONG_FORM; - public boolean notSet() { - return this == NOT_SET; - } - - public boolean setEmpty() { - return this == SET_EMPTY; + public boolean set() { + return this == SET_SHORT_FORM || this == SET_LONG_FORM; } } @@ -348,12 +323,8 @@ enum DefaultProperty { SET_LONG_FORM; - public boolean notSet() { - return this == NOT_SET; - } - - public boolean setEmpty() { - return this == SET_EMPTY; + public boolean set() { + return this == SET_SHORT_FORM || this == SET_LONG_FORM; } } diff --git a/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorPropertyMapTest.java b/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorPropertyMapTest.java index 178cf6c1c1..53f8f207f7 100644 --- a/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorPropertyMapTest.java +++ b/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorPropertyMapTest.java @@ -59,48 +59,34 @@ * Expected Required Result * * - * not set + * not set / A= * no * - * {} * Exception * * - * not set + * not set / A= * yes - * not set - * Exception - * Exception - * - * - * not set - * yes - * X= + * not set / X= * {} * Exception * * - * not set + * not set / A= * yes * X=Y:y,Z:z * {Y=y, Z=z} * {Y=y, Z=z} * * - * not set + * not set / A= * yes * X.Y=y, X.Z=z * {Y=y, Z=z} * {Y=y, Z=z} * * - * A= - * - - * - - * {} - * Exception - * - * * A=B:b,C:c * - * - @@ -240,7 +226,7 @@ void test(boolean required, ValueProperty valueProperty, // log all relevant configuration entries LOGGER.info("CONFIGURATION PROPERTIES"); - Map propertiesMap = MCRConfiguration2.getPropertiesMap(); + Map propertiesMap = MCRConfiguration2.getRawProperties(); LOGGER.info("{}={}", CONFIGURED_CLASS_PROPERTY, get(propertiesMap, CONFIGURED_CLASS_PROPERTY)); LOGGER.info("{}={}", MAP_PROPERTY, get(propertiesMap, MAP_PROPERTY)); LOGGER.info("{}={}", MAP_PROPERTY_NON_EMPTY, get(propertiesMap, MAP_PROPERTY_NON_EMPTY)); @@ -258,31 +244,24 @@ void test(boolean required, ValueProperty valueProperty, exception = e; } - boolean missingDefaultConfiguration = false; - missingDefaultConfiguration |= valueProperty.notSet() && defaultValue && defaultProperty.notSet(); - missingDefaultConfiguration |= required && valueProperty.notSet() && defaultValue && defaultProperty.setEmpty(); - - // all the indications a nested property should not be created (or creation should be suppressed) - boolean shouldNotCreateProperty = false; - shouldNotCreateProperty |= valueProperty.notSet() && !defaultValue; - shouldNotCreateProperty |= valueProperty.notSet() && defaultValue && defaultProperty.setEmpty(); - shouldNotCreateProperty |= valueProperty.setEmpty(); + boolean missingDefault = required && !valueProperty.set() && defaultValue && !defaultProperty.set(); + boolean emptyResultExpected = !valueProperty.set() && (!defaultValue || !defaultProperty.set()); - if (missingDefaultConfiguration) { + if (missingDefault) { assertNull(instance); assertNotNull(exception); - assertEquals("Default property map, configured in MCR.Map (and its sub-properties)," + assertEquals("Default property map, configured in MCR.Map (and sub-properties thereof)," + " for target field 'map' in configured class " + configuredClass.getName() + " is empty", exception.getMessage()); - } else if (required && shouldNotCreateProperty) { + } else if (required && emptyResultExpected) { assertNull(instance); assertNotNull(exception); - assertEquals("Property map, configured in Foo.Map (and its sub-properties)," + assertEquals("Property map, configured in Foo.Map (and sub-properties thereof)," + " for target field 'map' in configured class " + configuredClass.getName() + " is empty", exception.getMessage()); @@ -294,7 +273,7 @@ void test(boolean required, ValueProperty valueProperty, Map list = instance.map(); assertNotNull(list); - if (shouldNotCreateProperty) { + if (emptyResultExpected) { assertTrue(list.isEmpty()); @@ -304,10 +283,10 @@ void test(boolean required, ValueProperty valueProperty, String value = list.get("nonEmpty"); assertNotNull(value); - if (valueProperty.notSet()) { - assertEquals("DefaultValue", value); - } else { + if (valueProperty.set()) { assertEquals("Value", value); + } else { + assertEquals("DefaultValue", value); } } @@ -331,12 +310,8 @@ enum ValueProperty { SET_LONG_FORM; - public boolean notSet() { - return this == NOT_SET; - } - - public boolean setEmpty() { - return this == SET_EMPTY; + public boolean set() { + return this == SET_SHORT_FORM || this == SET_LONG_FORM; } } @@ -351,12 +326,8 @@ enum DefaultProperty { SET_LONG_FORM; - public boolean notSet() { - return this == NOT_SET; - } - - public boolean setEmpty() { - return this == SET_EMPTY; + public boolean set() { + return this == SET_SHORT_FORM || this == SET_LONG_FORM; } } diff --git a/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorPropertyTest.java b/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorPropertyTest.java index 647ddd16ee..02e4215c63 100644 --- a/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorPropertyTest.java +++ b/mycore-base/src/test/java/org/mycore/common/config/MCRInstantiatorPropertyTest.java @@ -56,41 +56,27 @@ * Expected Required Result * * - * not set + * not set / empty * no * - * null * Exception * * - * not set + * not set / empty * yes - * not set + * not set / empty * Exception * Exception * * - * not set - * yes - * empty - * empty - * empty - * - * - * not set + * not set / empty * yes * DefaultValue * DefaultValue * DefaultValue * * - * empty - * - - * - - * empty - * empty - * - * * Value * - * - @@ -179,7 +165,7 @@ void test(boolean required, ValueProperty valueProperty, // log all relevant configuration entries LOGGER.info("CONFIGURATION PROPERTIES"); - Map propertiesMap = MCRConfiguration2.getPropertiesMap(); + Map propertiesMap = MCRConfiguration2.getRawProperties(); LOGGER.info("{}={}", CONFIGURED_CLASS_PROPERTY, get(propertiesMap, CONFIGURED_CLASS_PROPERTY)); LOGGER.info("{}={}", VALUE_PROPERTY, get(propertiesMap, VALUE_PROPERTY)); LOGGER.info("{}={}", DEFAULT_VALUE_PROPERTY, get(propertiesMap, DEFAULT_VALUE_PROPERTY)); @@ -193,26 +179,24 @@ void test(boolean required, ValueProperty valueProperty, exception = e; } - boolean missingDefaultConfiguration = valueProperty.notSet() && defaultValue && defaultProperty.notSet(); + boolean missingDefault = required && !valueProperty.set() && defaultValue && !defaultProperty.set(); + boolean nullResultExpected = !valueProperty.set() && (!defaultValue || !defaultProperty.set()); - // all the indications a nested property should not be created (or creation should be suppressed) - boolean shouldNotCreateProperty = valueProperty.notSet() && !defaultValue; - - if (missingDefaultConfiguration) { + if (missingDefault) { assertNull(instance); assertNotNull(exception); - assertEquals("Default property, configured in MCR.Value (and its sub-properties)," + assertEquals("Default property, configured in MCR.Value (and sub-properties thereof)," + " for target field 'value' in configured class " + configuredClass.getName() + " is missing", exception.getMessage()); - } else if (required && shouldNotCreateProperty) { + } else if (required && nullResultExpected) { assertNull(instance); assertNotNull(exception); - assertEquals("Property, configured in Foo.Value (and its sub-properties)," + assertEquals("Property, configured in Foo.Value (and sub-properties thereof)," + " for target field 'value' in configured class " + configuredClass.getName() + " is missing", exception.getMessage()); @@ -223,7 +207,7 @@ void test(boolean required, ValueProperty valueProperty, String value = instance.value(); - if (shouldNotCreateProperty) { + if (nullResultExpected) { assertNull(value); @@ -231,22 +215,10 @@ void test(boolean required, ValueProperty valueProperty, assertNotNull(value); - if (valueProperty.notSet()) { - - if (defaultProperty.setEmpty()) { - assertEquals("", value); - } else { - assertEquals("DefaultValue", value); - } - + if (valueProperty.set()) { + assertEquals("Value", value); } else { - - if (valueProperty.setEmpty()) { - assertEquals("", value); - } else { - assertEquals("Value", value); - } - + assertEquals("DefaultValue", value); } } @@ -268,12 +240,8 @@ enum ValueProperty { SET_NON_EMPTY; - public boolean notSet() { - return this == NOT_SET; - } - - public boolean setEmpty() { - return this == SET_EMPTY; + public boolean set() { + return this == SET_NON_EMPTY; } } @@ -286,12 +254,8 @@ enum DefaultProperty { SET_NON_EMPTY; - public boolean notSet() { - return this == NOT_SET; - } - - public boolean setEmpty() { - return this == SET_EMPTY; + public boolean set() { + return this == SET_NON_EMPTY; } } diff --git a/mycore-base/src/test/java/org/mycore/common/config/MCRPropertiesURIResolverTest.java b/mycore-base/src/test/java/org/mycore/common/config/MCRPropertiesURIResolverTest.java index e56c83db2b..7b2946e347 100644 --- a/mycore-base/src/test/java/org/mycore/common/config/MCRPropertiesURIResolverTest.java +++ b/mycore-base/src/test/java/org/mycore/common/config/MCRPropertiesURIResolverTest.java @@ -32,7 +32,7 @@ public void resolve() { MCRConfiguration2.set("Sample.basedir", "/home/user/base"); MCRConfiguration2.set("Sample.subdir", "%Sample.basedir%/subdir"); MCRConfiguration2.set("Sample.file", "%Sample.subdir%/file.txt"); - MCRPropertiesResolver resolver = new MCRPropertiesResolver(MCRConfiguration2.getPropertiesMap()); + MCRPropertiesResolver resolver = new MCRPropertiesResolver(MCRConfiguration2.getRawProperties()); assertEquals("/home/user/base", resolver.resolve("%Sample.basedir%")); assertEquals("/home/user/base/subdir", resolver.resolve("%Sample.subdir%")); assertEquals("/home/user/base/subdir/file.txt", resolver.resolve("%Sample.file%")); @@ -43,7 +43,7 @@ public void resolveAll() { MCRConfiguration2.set("Sample.basedir", "/home/user/base"); MCRConfiguration2.set("Sample.subdir", "%Sample.basedir%/subdir"); MCRConfiguration2.set("Sample.file", "%Sample.subdir%/file.txt"); - Map p = MCRConfiguration2.getPropertiesMap(); + Map p = MCRConfiguration2.getRawProperties(); MCRPropertiesResolver resolver = new MCRPropertiesResolver(p); Map resolvedProperties = resolver.resolveAll(p); assertEquals("/home/user/base/subdir", resolvedProperties.get("Sample.subdir")); @@ -55,7 +55,7 @@ public void selfReference() { MCRConfiguration2.set("a", "%a%,hallo"); MCRConfiguration2.set("b", "hallo,%b%,welt"); MCRConfiguration2.set("c", "%b%,%a%"); - Map p = MCRConfiguration2.getPropertiesMap(); + Map p = MCRConfiguration2.getRawProperties(); MCRPropertiesResolver resolver = new MCRPropertiesResolver(p); assertEquals("hallo", resolver.resolve("%a%")); assertEquals("hallo,welt", resolver.resolve("%b%")); diff --git a/mycore-jobqueue/src/main/java/org/mycore/services/queuedjob/MCRSimpleJobSelector.java b/mycore-jobqueue/src/main/java/org/mycore/services/queuedjob/MCRSimpleJobSelector.java index 627dcc4b81..82b4d2a5be 100644 --- a/mycore-jobqueue/src/main/java/org/mycore/services/queuedjob/MCRSimpleJobSelector.java +++ b/mycore-jobqueue/src/main/java/org/mycore/services/queuedjob/MCRSimpleJobSelector.java @@ -32,10 +32,8 @@ import java.util.function.Supplier; import java.util.stream.Collectors; -import org.mycore.common.MCRClassTools; -import org.mycore.common.config.MCRConfigurationException; +import org.mycore.common.config.annotation.MCRClassPropertyList; import org.mycore.common.config.annotation.MCRConfigurationProxy; -import org.mycore.common.config.annotation.MCRPostConstruction; import org.mycore.common.config.annotation.MCRProperty; import org.mycore.common.config.annotation.MCRPropertyList; @@ -155,8 +153,9 @@ public enum Mode { public static class Factory implements Supplier { - @MCRPropertyList(name = ACTIONS_KEY, defaultName = "MCR.QueuedJob.Selectors.Default.Actions") - public List actions; + @MCRClassPropertyList(name = ACTIONS_KEY, valueClass = MCRJobAction.class, + defaultName = "MCR.QueuedJob.Selectors.Default.Actions") + public List> actions; @MCRProperty(name = ACTION_MODE_KEY, defaultName = "MCR.QueuedJob.Selectors.Default.ActionMode") public String actionMode; @@ -170,16 +169,9 @@ public static class Factory implements Supplier { @MCRProperty(name = AGE_DAYS_KEY, defaultName = "MCR.QueuedJob.Selectors.Default.AgeDays") public String ageDays; - private String property; - - @MCRPostConstruction - public void init(String property) { - this.property = property; - } - @Override public MCRSimpleJobSelector get() { - Set> actions = getActions(); + Set> actions = new HashSet<>(this.actions); Mode actionMode = Mode.valueOf(this.actionMode); Set statuses = getJobStatuses(); Mode statusMode = Mode.valueOf(this.statusMode); @@ -187,19 +179,6 @@ public MCRSimpleJobSelector get() { return new MCRSimpleJobSelector(actions, actionMode, statuses, statusMode, ageDays); } - private Set> getActions() { - return this.actions.stream().map(this::toActionJobClass).collect(Collectors.toSet()); - } - - private Class toActionJobClass(String action) { - try { - return MCRClassTools.forName(action); - } catch (ClassNotFoundException e) { - throw new MCRConfigurationException("Missing class (" + action + ") configured in: " + - property + "." + ACTIONS_KEY, e); - } - } - private Set getJobStatuses() { return this.statuses.stream().map(MCRJobStatus::valueOf).collect(Collectors.toSet()); } diff --git a/mycore-oai/src/main/java/org/mycore/oai/MCROAIIdentify.java b/mycore-oai/src/main/java/org/mycore/oai/MCROAIIdentify.java index cb0ecc1602..50ba2902f7 100644 --- a/mycore-oai/src/main/java/org/mycore/oai/MCROAIIdentify.java +++ b/mycore-oai/src/main/java/org/mycore/oai/MCROAIIdentify.java @@ -94,7 +94,7 @@ public String getConfigPrefix() { private Collection getDescriptionURIs() { String descriptionConfig = getConfigPrefix() + "DescriptionURI"; - return MCRConfiguration2.getPropertiesMap() + return MCRConfiguration2.getRawProperties() .entrySet() .stream() .filter(p -> p.getKey().startsWith(descriptionConfig)) @@ -104,7 +104,7 @@ private Collection getDescriptionURIs() { public FriendsDescription getFriendsDescription() { FriendsDescription desc = new FriendsDescription(); - MCRConfiguration2.getPropertiesMap() + MCRConfiguration2.getRawProperties() .entrySet() .stream() .filter(p -> p.getKey().startsWith(this.configPrefix + "Friends.")) diff --git a/mycore-oai/src/main/java/org/mycore/oai/classmapping/MCRClassificationAndSetMapper.java b/mycore-oai/src/main/java/org/mycore/oai/classmapping/MCRClassificationAndSetMapper.java index db011e7a1a..b58ee3082f 100644 --- a/mycore-oai/src/main/java/org/mycore/oai/classmapping/MCRClassificationAndSetMapper.java +++ b/mycore-oai/src/main/java/org/mycore/oai/classmapping/MCRClassificationAndSetMapper.java @@ -48,7 +48,7 @@ public class MCRClassificationAndSetMapper { */ public static String mapClassificationToSet(String prefix, String classid) { String propPrefix = prefix + PROP_SETS_PREFIX; - return MCRConfiguration2.getPropertiesMap() + return MCRConfiguration2.getRawProperties() .entrySet() .stream() .filter(p -> p.getKey().startsWith(propPrefix)) @@ -61,7 +61,7 @@ public static String mapClassificationToSet(String prefix, String classid) { } private static String getSetNameFromDeprecatedProperty(String prefix, String classid) { - return MCRConfiguration2.getPropertiesMap() + return MCRConfiguration2.getRawProperties() .entrySet() .stream() .filter(p -> p.getKey().startsWith(prefix + PROP_SUFFIX)) diff --git a/mycore-packer/src/main/java/org/mycore/services/packaging/MCRPackerJobAction.java b/mycore-packer/src/main/java/org/mycore/services/packaging/MCRPackerJobAction.java index 7b1a3d5467..2978e347ad 100644 --- a/mycore-packer/src/main/java/org/mycore/services/packaging/MCRPackerJobAction.java +++ b/mycore-packer/src/main/java/org/mycore/services/packaging/MCRPackerJobAction.java @@ -87,7 +87,7 @@ protected final Map getParameters() { public static Map getConfiguration(String packerId) { String packerConfigPrefix = MCRPacker.PACKER_CONFIGURATION_PREFIX + packerId + "."; - return MCRConfiguration2.getPropertiesMap() + return MCRConfiguration2.getRawProperties() .entrySet() .stream() .filter(p -> p.getKey().startsWith(packerConfigPrefix)) diff --git a/mycore-pi/src/main/java/org/mycore/pi/MCRPIConfigurationChecker.java b/mycore-pi/src/main/java/org/mycore/pi/MCRPIConfigurationChecker.java index b6fa070168..9b267ec29a 100644 --- a/mycore-pi/src/main/java/org/mycore/pi/MCRPIConfigurationChecker.java +++ b/mycore-pi/src/main/java/org/mycore/pi/MCRPIConfigurationChecker.java @@ -56,7 +56,7 @@ public void startUp(ServletContext servletContext) { LOGGER.info("Check persistent identifier configuration!"); final List deprecatedPropertyList = DEPRECATED_PROPERTY_PREFIXES .stream() - .flatMap(propPrefix -> MCRConfiguration2.getPropertiesMap() + .flatMap(propPrefix -> MCRConfiguration2.getRawProperties() .entrySet() .stream() .filter(p -> p.getKey().startsWith(propPrefix)) diff --git a/mycore-restapi/src/main/java/org/mycore/restapi/v2/MCRRestV2App.java b/mycore-restapi/src/main/java/org/mycore/restapi/v2/MCRRestV2App.java index e0be7cd08c..69bf0207bf 100644 --- a/mycore-restapi/src/main/java/org/mycore/restapi/v2/MCRRestV2App.java +++ b/mycore-restapi/src/main/java/org/mycore/restapi/v2/MCRRestV2App.java @@ -26,14 +26,11 @@ import java.util.stream.Stream; import org.glassfish.hk2.utilities.binding.AbstractBinder; -import org.mycore.common.MCRClassTools; import org.mycore.common.MCRCoreVersion; import org.mycore.common.config.MCRConfiguration2; -import org.mycore.common.config.MCRConfigurationException; +import org.mycore.common.config.annotation.MCRClassProperty; import org.mycore.common.config.annotation.MCRConfigurationProxy; import org.mycore.common.config.annotation.MCRInstanceMap; -import org.mycore.common.config.annotation.MCRPostConstruction; -import org.mycore.common.config.annotation.MCRProperty; import org.mycore.frontend.MCRFrontendUtil; import org.mycore.restapi.MCRApiDraftFilter; import org.mycore.restapi.MCRContentNegotiationViaExtensionFilter; @@ -146,40 +143,17 @@ public record Binding(Class implementationClass, Class apiClass) { public static final class Factory implements Supplier { - @MCRProperty(name = "From") - public String implementationClass; + @MCRClassProperty(name = "From") + public Class implementationClass; - @MCRProperty(name = "To") - public String apiClass; - - private String property; - - @MCRPostConstruction - public void init(String property) { - this.property = property; - } + @MCRClassProperty(name = "To") + public Class apiClass; @Override public Binding get() { - Class implementationClass = toClass(this.implementationClass, "From"); - Class apiClass = toClass(this.apiClass, "To"); - if (!apiClass.isAssignableFrom(implementationClass)) { - throw new IllegalArgumentException("Implementation " + this.implementationClass + " configured in " - + property + ".From does not implement/extend " + this.apiClass + " configured in " - + property + ".To"); - } return new Binding(implementationClass, apiClass); } - private Class toClass(String className, String key) { - try { - return MCRClassTools.forName(className); - } catch (ClassNotFoundException e) { - throw new MCRConfigurationException("Cannot load class " + className + "configured in " - + property + "." + key, e); - } - } - } } diff --git a/mycore-sword/src/main/java/org/mycore/sword/MCRSword.java b/mycore-sword/src/main/java/org/mycore/sword/MCRSword.java index 7ab9d6fb28..4d3577e5ac 100644 --- a/mycore-sword/src/main/java/org/mycore/sword/MCRSword.java +++ b/mycore-sword/src/main/java/org/mycore/sword/MCRSword.java @@ -57,7 +57,7 @@ private static void initConfig() { workspaceCollectionTable = new ConcurrentHashMap<>(); LOGGER.info("--- INITIALIZE SWORD SERVER ---"); final int lengthOfPropertyPrefix = MCRSwordConstants.MCR_SWORD_COLLECTION_PREFIX.length(); - MCRConfiguration2.getPropertiesMap() + MCRConfiguration2.getRawProperties() .keySet() .stream() .filter(prop -> prop.startsWith(MCRSwordConstants.MCR_SWORD_COLLECTION_PREFIX))