From dabb99d46f2655aa1ed13c633cfce6aaf7c97fad Mon Sep 17 00:00:00 2001 From: jensp Date: Fri, 18 Dec 2015 15:35:48 +0000 Subject: [PATCH] CCM NG: Database based configuration now passes all test cases. Not all planned features are yet implemented and tested (for example application instance specific configurations). They will be implemented and tested when they are needed. git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@3776 8810af33-2d31-482b-a856-94f89814c4df --- .../org/libreccm/categorization/Category.java | 6 +- .../categorization/CategoryRepository.java | 11 + .../configuration/ConfigurationManager.java | 441 +++++++++++------- .../ccm_core/h2/V7_0_0_0__create_tables.sql | 2 +- .../pgsql/V7_0_0_0__create_tables.sql | 2 +- .../categorization/CategoryManagerTest.java | 51 +- .../CategoryRepositoryTest.java | 26 +- .../libreccm/categorization/DatasetsTest.java | 1 + .../scripts/create_ccm_core_schema.sql | 2 +- .../scripts/create_ccm_core_schema.sql | 2 +- .../after-create-multiple-categories.yml | 101 ++++ .../after-save-new.yml | 91 ++-- .../test/resources/scripts/pgsql-cleanup.sql | 86 ++-- 13 files changed, 561 insertions(+), 261 deletions(-) create mode 100644 ccm-core/src/test/resources/datasets/org/libreccm/categorization/CategoryManagerTest/after-create-multiple-categories.yml diff --git a/ccm-core/src/main/java/org/libreccm/categorization/Category.java b/ccm-core/src/main/java/org/libreccm/categorization/Category.java index f7f394327..a525e6e0a 100644 --- a/ccm-core/src/main/java/org/libreccm/categorization/Category.java +++ b/ccm-core/src/main/java/org/libreccm/categorization/Category.java @@ -76,7 +76,7 @@ public class Category extends CcmObject implements Serializable { * A unique ID for the category. This ID will be the same even the same * category system/domain is used in different installations. */ - @Column(name = "UNIQUE_ID", nullable = false) + @Column(name = "UNIQUE_ID") private String uniqueId; /** @@ -371,8 +371,7 @@ public class Category extends CcmObject implements Serializable { + "title = %s, " + "enabled = %b, " + "visible = %b, " - + "abstractCategory = %b, " - + "parentCategory = %s, " + + "abstractCategory = %s, " + "categoryOrder = %d%s", uniqueId, name, @@ -380,7 +379,6 @@ public class Category extends CcmObject implements Serializable { enabled, visible, abstractCategory, - Objects.toString(parentCategory), categoryOrder, data)); } diff --git a/ccm-core/src/main/java/org/libreccm/categorization/CategoryRepository.java b/ccm-core/src/main/java/org/libreccm/categorization/CategoryRepository.java index 4f1526dfc..66d50f4f7 100644 --- a/ccm-core/src/main/java/org/libreccm/categorization/CategoryRepository.java +++ b/ccm-core/src/main/java/org/libreccm/categorization/CategoryRepository.java @@ -27,8 +27,10 @@ import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.persistence.NoResultException; import javax.persistence.TypedQuery; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.message.FormattedMessage; /** * @@ -124,9 +126,18 @@ public class CategoryRepository extends AbstractEntityRepository final String[] tokens = normalizedPath.split("/"); Category current = domain.getRoot(); for (String token : tokens) { + if (current.getSubCategories() == null) { + LOGGER.error(new FormattedMessage("#findByPath(Domain, String): current category \"%s\" has no sub categories", current.getName())); + } final Optional result = current.getSubCategories() .stream() .filter((c) -> { + LOGGER.debug(new FormattedMessage( + "#findByPath(Domain, String): c = %s", c.toString())); + LOGGER.debug(new FormattedMessage( + "#findByPath(Domain, String): c.getName = \"%s\"", c.getName())); + LOGGER.debug(new FormattedMessage( + "#findByPath(Domain, String): token = \"%s\"", token)); return c.getName().equals(token); }) .findFirst(); diff --git a/ccm-core/src/main/java/org/libreccm/configuration/ConfigurationManager.java b/ccm-core/src/main/java/org/libreccm/configuration/ConfigurationManager.java index 0b8e7c1c2..cbd33f56b 100644 --- a/ccm-core/src/main/java/org/libreccm/configuration/ConfigurationManager.java +++ b/ccm-core/src/main/java/org/libreccm/configuration/ConfigurationManager.java @@ -43,8 +43,15 @@ import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.UUID; + import org.apache.logging.log4j.message.FormattedMessage; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.StringJoiner; + /** * Maps between configuration classes and the values stored in the registry. * @@ -54,7 +61,7 @@ import org.apache.logging.log4j.message.FormattedMessage; public class ConfigurationManager { private static final Logger LOGGER = LogManager.getLogger( - ConfigurationManager.class); + ConfigurationManager.class); @Inject private CategoryManager categoryManager; @@ -71,11 +78,11 @@ public class ConfigurationManager { /** * Load all settings of the provided configuration class. * - * @param Type of the configuration class. + * @param Type of the configuration class. * @param confClass The configuration class. * * @return An instance of the configuration class with all settings set to - * the values stored in the registry. + * the values stored in the registry. */ public T findConfiguration(final Class confClass) { if (confClass == null) { @@ -84,9 +91,9 @@ public class ConfigurationManager { if (confClass.getAnnotation(Configuration.class) == null) { throw new IllegalArgumentException(String.format( - "Provided class \"%s\" is not annotated with \"%s\".", - confClass.getName(), - Configuration.class.getName())); + "Provided class \"%s\" is not annotated with \"%s\".", + confClass.getName(), + Configuration.class.getName())); } final String confName = confClass.getName(); @@ -99,11 +106,13 @@ public class ConfigurationManager { * registry. * * @param configuration The configuration to save. The class of the provided - * object must be annotation with {@link Configuration}. + * object must be annotation with + * {@link Configuration}. * * @throws IllegalArgumentException If the {@code configuration} parameter - * is {@code null} or if the class of the provided object is not annotation - * with {@link Configuration}. + * is {@code null} or if the class of the + * provided object is not annotation with + * {@link Configuration}. */ public void saveConfiguration(final Object configuration) { if (configuration == null) { @@ -112,10 +121,10 @@ public class ConfigurationManager { if (configuration.getClass().getAnnotation(Configuration.class) == null) { throw new IllegalArgumentException(String.format( - "The class \"%s\" of the provided object is not annotated " - + "with \"%s\".", - configuration.getClass().getName(), - Configuration.class.getName())); + "The class \"%s\" of the provided object is not annotated " + + "with \"%s\".", + configuration.getClass().getName(), + Configuration.class.getName())); } LOGGER.debug(String.format("Saving configuration \"%s\"...", @@ -126,10 +135,10 @@ public class ConfigurationManager { if (field.getAnnotation(Setting.class) == null) { LOGGER.debug(String.format( - "Field \"%s\" of class \"%s\" is not " - + "a setting. Ignoring it.", - configuration.getClass().getName(), - field.getName())); + "Field \"%s\" of class \"%s\" is not " + + "a setting. Ignoring it.", + configuration.getClass().getName(), + field.getName())); continue; } @@ -140,16 +149,16 @@ public class ConfigurationManager { field.get(configuration)); } catch (IllegalAccessException ex) { LOGGER.error(String.format( - "Failed to write setting value for setting \"%s\" " - + "of configuration \"%s\"", - getSettingName(field), - configuration.getClass().getName()), + "Failed to write setting value for setting \"%s\" " + + "of configuration \"%s\"", + getSettingName(field), + configuration.getClass().getName()), ex); throw new IllegalStateException(String.format( - "Failed to write setting value for setting \"%s\" " - + "of configuration \"%s\"", - getSettingName(field), - configuration.getClass().getName()), + "Failed to write setting value for setting \"%s\" " + + "of configuration \"%s\"", + getSettingName(field), + configuration.getClass().getName()), ex); } } @@ -159,15 +168,15 @@ public class ConfigurationManager { * Finds an application instance specific configuration and loads it values * from the registry. * - * @param The type of the configuration. + * @param The type of the configuration. * @param confClass The configuration class. - * @param instance The application instance for which the settings are - * loaded. + * @param instance The application instance for which the settings are + * loaded. * * @return The configuration for the provided application instance. */ public T findConfiguration( - final Class confClass, final CcmApplication instance) { + final Class confClass, final CcmApplication instance) { if (confClass == null) { throw new IllegalArgumentException("confClass can't be null"); } @@ -178,9 +187,9 @@ public class ConfigurationManager { if (confClass.getAnnotation(Configuration.class) == null) { throw new IllegalArgumentException(String.format( - "Provided class \"%s\" is not annotated with \"%s\".", - confClass.getName(), - Configuration.class.getName())); + "Provided class \"%s\" is not annotated with \"%s\".", + confClass.getName(), + Configuration.class.getName())); } final String confName = String.format("%s.%s", @@ -194,8 +203,8 @@ public class ConfigurationManager { * Saves a application instance configuration. * * @param configuration The configuration to save. - * @param instance The application instance of which the configuration - * stores the settings. + * @param instance The application instance of which the configuration + * stores the settings. */ public void saveConfiguration(final ApplicationConfiguration configuration, final CcmApplication instance) { @@ -205,10 +214,10 @@ public class ConfigurationManager { if (configuration.getClass().getAnnotation(Configuration.class) == null) { throw new IllegalArgumentException(String.format( - "The class \"%s\" of the provided object is not annotated " - + "with \"%s\".", - configuration.getClass().getName(), - Configuration.class.getName())); + "The class \"%s\" of the provided object is not annotated " + + "with \"%s\".", + configuration.getClass().getName(), + Configuration.class.getName())); } if (instance == null) { @@ -226,16 +235,16 @@ public class ConfigurationManager { field.get(configuration)); } catch (IllegalAccessException ex) { LOGGER.error(String.format( - "Failed to write setting value for setting \"%s\" " - + "of configuration \"%s\"", - getSettingName(field), - configuration.getClass().getName()), + "Failed to write setting value for setting \"%s\" " + + "of configuration \"%s\"", + getSettingName(field), + configuration.getClass().getName()), ex); throw new IllegalStateException(String.format( - "Failed to write setting value for setting \"%s\" " - + "of configuration \"%s\"", - getSettingName(field), - configuration.getClass().getName()), + "Failed to write setting value for setting \"%s\" " + + "of configuration \"%s\"", + getSettingName(field), + configuration.getClass().getName()), ex); } } @@ -247,7 +256,7 @@ public class ConfigurationManager { * @param configuration The configuration for which the info is generated. * * @return a {@link ConfigurationInfo} instance describing the provided - * configuration. + * configuration. */ public ConfigurationInfo getConfigurationInfo(final Class configuration) { if (configuration == null) { @@ -256,14 +265,14 @@ public class ConfigurationManager { if (configuration.getAnnotation(Configuration.class) == null) { throw new IllegalArgumentException(String.format( - "The class \"%s\" of the provided object is not annotated " - + "with \"%s\".", - configuration.getClass().getName(), - Configuration.class.getName())); + "The class \"%s\" of the provided object is not annotated " + + "with \"%s\".", + configuration.getClass().getName(), + Configuration.class.getName())); } final Configuration annotation = configuration.getAnnotation( - Configuration.class); + Configuration.class); final ConfigurationInfo confInfo = new ConfigurationInfo(); confInfo.setName(configuration.getClass().getName()); @@ -286,9 +295,9 @@ public class ConfigurationManager { * Create a {@link SettingInfo} instance for a setting. * * @param configuration The configuration class to which the settings - * belongs. - * @param name The name of the setting for which the {@link SettingInfo} is - * generated. + * belongs. + * @param name The name of the setting for which the + * {@link SettingInfo} is generated. * * @return The {@link SettingInfo} for the provided configuration class. */ @@ -300,14 +309,14 @@ public class ConfigurationManager { if (configuration.getAnnotation(Configuration.class) == null) { throw new IllegalArgumentException(String.format( - "The class \"%s\" of the provided object is not annotated " - + "with \"%s\".", - configuration.getClass().getName(), - Configuration.class.getName())); + "The class \"%s\" of the provided object is not annotated " + + "with \"%s\".", + configuration.getClass().getName(), + Configuration.class.getName())); } final Configuration confAnnotation = configuration.getAnnotation( - Configuration.class); + Configuration.class); final String descBundle = confAnnotation.descBundle(); final Field field; @@ -315,10 +324,10 @@ public class ConfigurationManager { field = configuration.getDeclaredField(name); } catch (SecurityException | NoSuchFieldException ex) { LOGGER.warn(String.format( - "Failed to generate SettingInfo for field \"%s\" of " - + "configuration \"%s\". Ignoring field.", - configuration.getClass().getName(), - name), + "Failed to generate SettingInfo for field \"%s\" of " + + "configuration \"%s\". Ignoring field.", + configuration.getClass().getName(), + name), ex); return null; } @@ -330,7 +339,7 @@ public class ConfigurationManager { final Setting settingAnnotation = field.getAnnotation(Setting.class); final SettingInfo settingInfo = new SettingInfo(); if (settingAnnotation.name() == null - || settingAnnotation.name().isEmpty()) { + || settingAnnotation.name().isEmpty()) { settingInfo.setName(field.getName()); } else { settingInfo.setName(settingAnnotation.name()); @@ -343,7 +352,7 @@ public class ConfigurationManager { settingInfo.setDefaultValue(field.get(conf).toString()); } catch (InstantiationException | IllegalAccessException ex) { LOGGER.warn(String.format("Failed to create instance of \"%s\" to " - + "get default values.", + + "get default values.", configuration.getName()), ex); } @@ -359,19 +368,19 @@ public class ConfigurationManager { /** * A low level method for finding a setting in the registry. * - * @param Type of the value of the setting - * @param name The fully qualified name of the setting. + * @param Type of the value of the setting + * @param name The fully qualified name of the setting. * @param clazz The class of the setting. * * @return The requested setting if it exists in the registry, {@code null} - * otherwise. + * otherwise. */ public AbstractSetting findSetting(final String name, final Class clazz) { LOGGER.debug(String.format( - "Trying to find setting \"%s\" of type \"%s\"", - name, - clazz.getName())); + "Trying to find setting \"%s\" of type \"%s\"", + name, + clazz.getName())); final String[] tokens = name.split("\\."); LOGGER.debug(String.format("Setting name \"%s\" has %d tokens.", name, @@ -384,29 +393,29 @@ public class ConfigurationManager { categoryPath)); final Domain registry = domainRepository - .findByDomainKey(REGISTRY_DOMAIN); + .findByDomainKey(REGISTRY_DOMAIN); final Category category = categoryRepository.findByPath(registry, categoryPath); if (category == null) { LOGGER.warn(String.format(String.format( - "Category \"%s\" for setting \"%s\" not found.", - categoryPath, - name))); + "Category \"%s\" for setting \"%s\" not found.", + categoryPath, + name))); return null; } LOGGER.debug(String.format("Category has %d objects. Filtering.", category.getObjects().size())); final Optional result = category - .getObjects() - .stream() - .filter((Categorization c) - -> c.getCategorizedObject() instanceof AbstractSetting) - .filter((Categorization c) - -> ((AbstractSetting) c.getCategorizedObject()) - .getName() - .equals(tokens[tokens.length - 1])) - .findFirst(); + .getObjects() + .stream() + .filter((Categorization c) + -> c.getCategorizedObject() instanceof AbstractSetting) + .filter((Categorization c) + -> ((AbstractSetting) c.getCategorizedObject()) + .getName() + .equals(tokens[tokens.length - 1])) + .findFirst(); if (result.isPresent()) { final CcmObject object = result.get().getCategorizedObject(); @@ -415,20 +424,20 @@ public class ConfigurationManager { if (clazz.isInstance(entry.getValue())) { @SuppressWarnings("unchecked") final AbstractSetting resultEntry - = (AbstractSetting) entry; + = (AbstractSetting) entry; return resultEntry; } else { LOGGER.warn(String.format("Setting \"%s\" found but is not of " - + "the requested type \"%s\".", + + "the requested type \"%s\".", name, clazz.getName())); return null; } } else { LOGGER.warn(String.format( - "Setting \"%s\" was not found in category \"%s\".", - name, - categoryPath)); + "Setting \"%s\" was not found in category \"%s\".", + name, + categoryPath)); return null; } } @@ -455,11 +464,11 @@ public class ConfigurationManager { * @param field The setting field. * * @return The name of the field or if the {@link Setting} annotation of the - * field has a name value, the value of that field. + * field has a name value, the value of that field. */ private String getSettingName(final Field field) { LOGGER.debug(String.format("Trying to get setting name from field: " - + "\"%s\"", + + "\"%s\"", field.getName())); final Setting annotation = field.getAnnotation(Setting.class); @@ -473,17 +482,17 @@ public class ConfigurationManager { /** * Create a setting instance of a specific value type. * - * @param Type variable. + * @param Type variable. * @param valueType The type of the value of the setting to create. * * @return An setting instance of the provided value type. * * @throws IllegalArgumentException If there is not setting type for the - * provided value type. + * provided value type. */ @SuppressWarnings("unchecked") private AbstractSetting createSettingForValueType( - final Class valueType) { + final Class valueType) { final String valueTypeName = valueType.getName(); if (BigDecimal.class.getName().equals(valueTypeName)) { @@ -496,59 +505,66 @@ public class ConfigurationManager { return (AbstractSetting) new EnumSetting(); } else if (LocalizedString.class.getName().equals(valueTypeName)) { return (AbstractSetting) new LocalizedStringSetting(); - } else if (LongSetting.class.getName().equals(valueTypeName)) { + } else if (Long.class.getName().equals(valueTypeName)) { return (AbstractSetting) new LongSetting(); } else if (String.class.getName().equals(valueTypeName)) { return (AbstractSetting) new StringSetting(); } else { throw new IllegalArgumentException(String.format( - "No setting type for value type \"s\".", valueTypeName)); + "No setting type for value type \"%s\".", valueTypeName)); } } /** * Sets a value on a setting in the registry. * - * @param The value type of the setting. + * @param The value type of the setting. * @param configuration The configuration to which the settings belongs. - * @param settingName The name of the setting. - * @param valueType The type of the value of the setting. - * @param value The value to set. + * @param settingName The name of the setting. + * @param valueType The type of the value of the setting. + * @param value The value to set. */ private void setSettingValue(final Object configuration, final String settingName, final Class valueType, final Object value) { final String settingPath = String.format( - "%s.%s", - configuration.getClass().getName(), - settingName); - LOGGER.debug(String.format("Saving setting \"%s\"...", settingPath)); + "%s.%s", + configuration.getClass().getName(), + settingName); + LOGGER.debug(new FormattedMessage( + "Saving setting \"%s\" of type \"%s\"...", + settingPath, + valueType.getName())); AbstractSetting setting = findSetting(settingPath, valueType); if (setting == null) { LOGGER.debug(String.format("Setting \"%s\" does not yet exist in " - + "database. Creating new setting.", + + "database. Creating new setting.", settingPath)); setting = createSettingForValueType(valueType); setting.setName(settingName); - final Domain registry = domainRepository - .findByDomainKey(REGISTRY_DOMAIN); - Category category = categoryRepository - .findByPath(registry, configuration.getClass().getName()); - if (category == null) { - final String[] tokens = configuration.getClass().getName(). - split("\\."); - final StringBuilder categoryPath = new StringBuilder( - configuration.getClass().getName().length()); - for (String token : tokens) { - if (categoryPath.length() > 0) { - categoryPath.append('.'); - } - categoryPath.append(token); - category = createCategoryIfNotExists(categoryPath.toString()); - } - } + setting.setDisplayName(settingName); + final Category category = findCategoryForNewSetting(configuration); categoryManager.addObjectToCategory(setting, category); + +// final Domain registry = domainRepository +// .findByDomainKey(REGISTRY_DOMAIN); +// Category category = categoryRepository +// .findByPath(registry, configuration.getClass().getName()); +// if (category == null) { +// final String[] tokens = configuration.getClass().getName(). +// split("\\."); +// final StringBuilder categoryPath = new StringBuilder( +// configuration.getClass().getName().length()); +// for (String token : tokens) { +// if (categoryPath.length() > 0) { +// categoryPath.append('.'); +// } +// categoryPath.append(token); +// category = createCategoryIfNotExists(categoryPath.toString()); +// } +// } +// categoryManager.addObjectToCategory(setting, category); } LOGGER.debug(String.format("New value of setting \"%s\" is: \"%s\"", @@ -563,17 +579,18 @@ public class ConfigurationManager { LOGGER.debug("Saving changed setting to DB..."); entityManager.merge(setting); + entityManager.flush(); } /** * Sets the value of a setting of application instance configuration. * - * @param The value type of the setting. + * @param The value type of the setting. * @param configuration The configuration to which the settings belongs. - * @param instance The application instance to which - * @param settingName The name of the setting. - * @param valueType The type of the value of the setting. - * @param value The value to set. + * @param instance The application instance to which + * @param settingName The name of the setting. + * @param valueType The type of the value of the setting. + * @param value The value to set. */ private void setSettingValue(final Object configuration, final CcmApplication instance, @@ -581,18 +598,18 @@ public class ConfigurationManager { final Class valueType, final Object value) { final String settingPath = String.format( - "%s.%s.%s", - configuration.getClass().getName(), - instance.getPrimaryUrl(), - settingName); + "%s.%s.%s", + configuration.getClass().getName(), + instance.getPrimaryUrl(), + settingName); AbstractSetting setting = findSetting(settingPath, valueType); if (setting == null) { setting = createSettingForValueType(valueType); setting.setName(settingName); final Domain registry = domainRepository - .findByDomainKey(REGISTRY_DOMAIN); + .findByDomainKey(REGISTRY_DOMAIN); final Category category = categoryRepository - .findByPath(registry, configuration.getClass().getName()); + .findByPath(registry, configuration.getClass().getName()); categoryManager.addObjectToCategory(setting, category); } @@ -606,21 +623,23 @@ public class ConfigurationManager { /** * Helper method for loading a configuration from the registry. * - * @param The type of the configuration. - * @param confName The fully qualified name of the configuration in the - * registry. For normal configuration this is the fully qualified name of - * the configuration class. For application instance configurations this is - * the fully qualified name of the configuration class joined with the - * primary URL of the application instance, separated with a dot. + * @param The type of the configuration. + * @param confName The fully qualified name of the configuration in the + * registry. For normal configuration this is the fully + * qualified name of the configuration class. For + * application instance configurations this is the fully + * qualified name of the configuration class joined with + * the primary URL of the application instance, separated + * with a dot. * @param confClass The configuration class. * * @return An instance of the configuration class with all setting fields - * set to the values stored in the registry. + * set to the values stored in the registry. */ private T findConfiguration(final String confName, final Class confClass) { final Domain registry = domainRepository - .findByDomainKey(REGISTRY_DOMAIN); + .findByDomainKey(REGISTRY_DOMAIN); final Category category = categoryRepository.findByPath(registry, confName); @@ -633,8 +652,8 @@ public class ConfigurationManager { conf = confClass.newInstance(); } catch (InstantiationException | IllegalAccessException ex) { LOGGER.warn(String.format( - "Failed to instantiate configuration \"%s\".", - confClass.getName()), + "Failed to instantiate configuration \"%s\".", + confClass.getName()), ex); return null; } @@ -655,15 +674,15 @@ public class ConfigurationManager { if (setting != null) { try { LOGGER.debug(String. - format("Setting \"%s\" found. Value: %s", - settingPath, - setting.getValue().toString())); + format("Setting \"%s\" found. Value: %s", + settingPath, + setting.getValue().toString())); field.set(conf, setting.getValue()); } catch (IllegalAccessException ex) { LOGGER.warn(String.format( - "Failed to set value of configuration class \"%s\". " + "Failed to set value of configuration class \"%s\". " + "Ignoring.", - confClass.getName()), + confClass.getName()), ex); } } @@ -672,13 +691,103 @@ public class ConfigurationManager { return conf; } + private Category findCategoryForNewSetting(final Object configuration) { + LOGGER.debug(new FormattedMessage( + "#findCategoryForNewSetting: Looking for category for " + + "configuration \"%s\"...", + configuration.getClass().getName())); + final String categoryPath = configuration.getClass().getName(); + final String[] tokens = categoryPath.split("\\."); + final Domain registry = domainRepository + .findByDomainKey(REGISTRY_DOMAIN); + + final Category[] categories = new Category[tokens.length]; + + //Check which of the categories in the categoryPath exist already + final boolean[] exists = new boolean[tokens.length]; + for (int i = 0; i < tokens.length; i++) { + final String path = buildCategoryPath(tokens, i); + LOGGER.debug(new FormattedMessage( + "#findCategoryForNewSetting: Checking if category \"%s\" exists.", + path)); + final Category category = categoryRepository.findByPath(registry, + path); + if (category == null) { + LOGGER.debug(new FormattedMessage( + "#findCategoryForNewSetting: Category \"%s\" does not exist.", + path)); + exists[i] = false; + } else { + LOGGER.debug(new FormattedMessage( + "#findCategoryForNewSetting: Category \"%s\" exists.", + path)); + exists[i] = true; + categories[i] = category; + } + } + + LOGGER.debug( + "#findCategoryForNewSetting: Creating missing categories..."); + for (int i = 0; i < tokens.length; i++) { + LOGGER.debug(new FormattedMessage( + "#findCategoryForNewSetting: Checking for category \"%s\"...", + tokens[i])); + if (!exists[i]) { + + if (i == 0) { + LOGGER.debug(new FormattedMessage( + "#findCategoryForNewSetting: Category \"%s\" does not exist, " + + "creating as subcategory of the registry root category.", + tokens[i])); + categories[i] = createNewCategory(tokens[i], + registry.getRoot()); + } else { + LOGGER.debug(new FormattedMessage( + "#findCategoryForNewSetting: Category \"%s\" does not exist, " + + "creating as subcategory of \"%s\"", + tokens[i], + categories[i - 1].getName())); + categories[i] = createNewCategory(tokens[i], + categories[i - 1]); + } + } + } + + LOGGER.debug(new FormattedMessage( + "#findCategoryForNewSetting: Found/Created category \"%s\".", + categoryPath)); + return categories[categories.length - 1]; + } + + private String buildCategoryPath(final String[] tokens, + final int index) { + final StringJoiner joiner = new StringJoiner("."); + for (int i = 0; i <= index; i++) { + joiner.add(tokens[i]); + } + + return joiner.toString(); + } + + private Category createNewCategory(final String name, + final Category parent) { + final Category category = new Category(); + category.setName(name); + category.setDisplayName(name); + categoryRepository.save(category); + entityManager.flush(); + categoryManager.addSubCategoryToCategory(category, parent); + + return category; + } + private Category createCategoryIfNotExists(final String categoryPath) { LOGGER.debug(String.format("Checking if category \"%s\" exists. If not " - + "the category will be created.", + + "the category will be created.", categoryPath)); final Domain registry = domainRepository. - findByDomainKey(REGISTRY_DOMAIN); + findByDomainKey(REGISTRY_DOMAIN); final Category root = registry.getRoot(); final String[] tokens = categoryPath.split("\\."); @@ -686,14 +795,16 @@ public class ConfigurationManager { categoryPath); if (category == null) { LOGGER.debug(String.format( - "Category \"%s\" was not found. Creating category.", - categoryPath)); + "Category \"%s\" was not found. Creating category.", + categoryPath)); category = new Category(); category.setName(tokens[tokens.length - 1]); category.setUniqueId(UUID.randomUUID().toString()); category.setEnabled(true); category.setVisible(true); category.setAbstractCategory(false); + categoryRepository.save(category); + entityManager.flush(); if (tokens.length > 1) { final StringBuilder parentPath = new StringBuilder(); for (int i = 0; i < tokens.length - 1; i++) { @@ -703,26 +814,26 @@ public class ConfigurationManager { parentPath.append(tokens); } final Category parent = categoryRepository.findByPath( - registry, - parentPath.toString()); + registry, + parentPath.toString()); if (parent == null) { throw new IllegalStateException(String.format( - "Parent category \"%s\" of for new category \"%s\" does" - + "not exist, but should. Can't continue.", - parentPath.toString(), - categoryPath)); + "Parent category \"%s\" for new category \"%s\" " + + "does not exist, but should. Can't continue.", + parentPath.toString(), + categoryPath)); } categoryManager.addSubCategoryToCategory(category, parent); LOGGER.debug(new FormattedMessage( - "Created category \"%s\" as child of category \"%s\".", - categoryPath, - parent.getName())); + "Created category \"%s\" as child of category \"%s\".", + categoryPath, + parent.getName())); } else { categoryManager.addSubCategoryToCategory(category, root); LOGGER.debug(new FormattedMessage( - "Created category \"%s\" as child of the registry root " - + "category.", - categoryPath)); + "Created category \"%s\" as child of the registry root " + + "category.", + categoryPath)); } } diff --git a/ccm-core/src/main/resources/db/migrations/org/libreccm/ccm_core/h2/V7_0_0_0__create_tables.sql b/ccm-core/src/main/resources/db/migrations/org/libreccm/ccm_core/h2/V7_0_0_0__create_tables.sql index 6c38ef1a8..ede6c4e31 100644 --- a/ccm-core/src/main/resources/db/migrations/org/libreccm/ccm_core/h2/V7_0_0_0__create_tables.sql +++ b/ccm-core/src/main/resources/db/migrations/org/libreccm/ccm_core/h2/V7_0_0_0__create_tables.sql @@ -21,7 +21,7 @@ CATEGORY_ORDER bigint, ENABLED boolean, NAME varchar(255) not null, - UNIQUE_ID varchar(255) not null, + UNIQUE_ID varchar(255), VISIBLE boolean, OBJECT_ID bigint not null, PARENT_CATEGORY_ID bigint, diff --git a/ccm-core/src/main/resources/db/migrations/org/libreccm/ccm_core/pgsql/V7_0_0_0__create_tables.sql b/ccm-core/src/main/resources/db/migrations/org/libreccm/ccm_core/pgsql/V7_0_0_0__create_tables.sql index 6fa300f29..3220547bc 100644 --- a/ccm-core/src/main/resources/db/migrations/org/libreccm/ccm_core/pgsql/V7_0_0_0__create_tables.sql +++ b/ccm-core/src/main/resources/db/migrations/org/libreccm/ccm_core/pgsql/V7_0_0_0__create_tables.sql @@ -21,7 +21,7 @@ CATEGORY_ORDER int8, ENABLED boolean, NAME varchar(255) not null, - UNIQUE_ID varchar(255) not null, + UNIQUE_ID varchar(255), VISIBLE boolean, OBJECT_ID int8 not null, PARENT_CATEGORY_ID int8, diff --git a/ccm-core/src/test/java/org/libreccm/categorization/CategoryManagerTest.java b/ccm-core/src/test/java/org/libreccm/categorization/CategoryManagerTest.java index 88b5627b9..ed23ca54c 100644 --- a/ccm-core/src/test/java/org/libreccm/categorization/CategoryManagerTest.java +++ b/ccm-core/src/test/java/org/libreccm/categorization/CategoryManagerTest.java @@ -224,9 +224,10 @@ public class CategoryManagerTest { @Test @UsingDataSet( "datasets/org/libreccm/categorization/CategoryManagerTest/data.yml") - @ShouldMatchDataSet(value - = "datasets/org/libreccm/categorization/CategoryManagerTest/after-remove-subcategory.yml", - excludeColumns = {"categorization_id", "object_id"}) + @ShouldMatchDataSet( + value = "datasets/org/libreccm/categorization/" + + "CategoryManagerTest/after-remove-subcategory.yml", + excludeColumns = {"categorization_id", "object_id"}) @InSequence(2200) public void removeSubCategoryToCategory() { final Category foo = categoryRepo.findById(-2100L); @@ -235,4 +236,48 @@ public class CategoryManagerTest { categoryManager.removeSubCategoryFromCategory(bar, foo); } + @Test + @UsingDataSet( + "datasets/org/libreccm/categorization/CategoryManagerTest/data.yml") + @ShouldMatchDataSet( + value = "datasets/org/libreccm/categorization/" + + "CategoryManagerTest/after-create-multiple-categories.yml" +// excludeColumns = {"categorization_id", +// "object_id", +// "parent_category_id"} + ) + @InSequence(3100) + public void createMultipleCategories() { + final Domain domain = domainRepo.findByDomainKey("test"); + final Category root = domain.getRoot(); + + final Category com = new Category(); + com.setName("com"); + com.setDisplayName("com"); + com.setUniqueId("com"); + categoryRepo.save(com); + categoryManager.addSubCategoryToCategory(com, root); + + final Category example = new Category(); + example.setName("example"); + example.setDisplayName("example"); + example.setUniqueId("example"); + categoryRepo.save(example); + categoryManager.addSubCategoryToCategory(example, com); + + final Category categories = new Category(); + categories.setName("categories"); + categories.setDisplayName("categories"); + categories.setUniqueId("categories"); + categoryRepo.save(categories); + categoryManager.addSubCategoryToCategory(categories, example); + + final Category test = new Category(); + test.setName("test"); + test.setDisplayName("test"); + test.setUniqueId("test"); + categoryRepo.save(test); + categoryManager.addSubCategoryToCategory(test, categories); + } + } diff --git a/ccm-core/src/test/java/org/libreccm/categorization/CategoryRepositoryTest.java b/ccm-core/src/test/java/org/libreccm/categorization/CategoryRepositoryTest.java index d24111932..25551841a 100644 --- a/ccm-core/src/test/java/org/libreccm/categorization/CategoryRepositoryTest.java +++ b/ccm-core/src/test/java/org/libreccm/categorization/CategoryRepositoryTest.java @@ -175,6 +175,17 @@ public class CategoryRepositoryTest { assertThat(notFound, is(nullValue())); } + @Test + @UsingDataSet( + "datasets/org/libreccm/categorization/CategoryRepositoryTest/data.yml") + @InSequence(1150) + public void findByPathStringNotExisting() { + final Category doesNotExist = categoryRepo.findByPath( + "test:/does/not/exist"); + + assertThat(doesNotExist, is(nullValue())); + } + @Test(expected = InvalidCategoryPathException.class) @UsingDataSet( "datasets/org/libreccm/categorization/CategoryRepositoryTest/data.yml") @@ -214,6 +225,19 @@ public class CategoryRepositoryTest { assertThat(notFound, is(nullValue())); } + @Test + @UsingDataSet( + "datasets/org/libreccm/categorization/CategoryRepositoryTest/data.yml") + @InSequence(1150) + public void findByPathDomainStringNotExisting() { + final Domain domain = domainRepo.findByDomainKey("test"); + + final Category doesNotExist = categoryRepo.findByPath(domain, + "/does/not/exist"); + + assertThat(doesNotExist, is(nullValue())); + } + @Test @UsingDataSet( "datasets/org/libreccm/categorization/CategoryRepositoryTest/data.yml") @@ -227,7 +251,7 @@ public class CategoryRepositoryTest { category.setDisplayName("new-category"); category.setName("new-category"); category.setUniqueId("new0001"); - + categoryRepo.save(category); } diff --git a/ccm-core/src/test/java/org/libreccm/categorization/DatasetsTest.java b/ccm-core/src/test/java/org/libreccm/categorization/DatasetsTest.java index 1448a4602..8a69a580c 100644 --- a/ccm-core/src/test/java/org/libreccm/categorization/DatasetsTest.java +++ b/ccm-core/src/test/java/org/libreccm/categorization/DatasetsTest.java @@ -42,6 +42,7 @@ public class DatasetsTest extends DatasetsVerifier { return Arrays.asList(new String[]{ "/datasets/org/libreccm/categorization/CategoryManagerTest/after-add-obj-to-category.yml", "/datasets/org/libreccm/categorization/CategoryManagerTest/after-add-subcategory.yml", + "/datasets/org/libreccm/categorization/CategoryManagerTest/after-create-multiple-categories.yml", "/datasets/org/libreccm/categorization/CategoryManagerTest/after-remove-obj-from-category.yml", "/datasets/org/libreccm/categorization/CategoryManagerTest/after-remove-subcategory.yml", "/datasets/org/libreccm/categorization/CategoryManagerTest/data.yml", diff --git a/ccm-core/src/test/resources-wildfly8-remote-h2-mem/scripts/create_ccm_core_schema.sql b/ccm-core/src/test/resources-wildfly8-remote-h2-mem/scripts/create_ccm_core_schema.sql index 323821ea8..9d90744fc 100644 --- a/ccm-core/src/test/resources-wildfly8-remote-h2-mem/scripts/create_ccm_core_schema.sql +++ b/ccm-core/src/test/resources-wildfly8-remote-h2-mem/scripts/create_ccm_core_schema.sql @@ -27,7 +27,7 @@ CREATE SCHEMA ccm_core; CATEGORY_ORDER bigint, ENABLED boolean, NAME varchar(255) not null, - UNIQUE_ID varchar(255) not null, + UNIQUE_ID varchar(255), VISIBLE boolean, OBJECT_ID bigint not null, PARENT_CATEGORY_ID bigint, diff --git a/ccm-core/src/test/resources-wildfly8-remote-pgsql/scripts/create_ccm_core_schema.sql b/ccm-core/src/test/resources-wildfly8-remote-pgsql/scripts/create_ccm_core_schema.sql index cb2a83440..3174a72b6 100644 --- a/ccm-core/src/test/resources-wildfly8-remote-pgsql/scripts/create_ccm_core_schema.sql +++ b/ccm-core/src/test/resources-wildfly8-remote-pgsql/scripts/create_ccm_core_schema.sql @@ -27,7 +27,7 @@ CREATE SCHEMA ccm_core; CATEGORY_ORDER int8, ENABLED boolean, NAME varchar(255) not null, - UNIQUE_ID varchar(255) not null, + UNIQUE_ID varchar(255), VISIBLE boolean, OBJECT_ID int8 not null, PARENT_CATEGORY_ID int8, diff --git a/ccm-core/src/test/resources/datasets/org/libreccm/categorization/CategoryManagerTest/after-create-multiple-categories.yml b/ccm-core/src/test/resources/datasets/org/libreccm/categorization/CategoryManagerTest/after-create-multiple-categories.yml new file mode 100644 index 000000000..2cb591c62 --- /dev/null +++ b/ccm-core/src/test/resources/datasets/org/libreccm/categorization/CategoryManagerTest/after-create-multiple-categories.yml @@ -0,0 +1,101 @@ +ccm_core.ccm_objects: + - object_id: -1000 + display_name: test + - object_id: -2000 + display_name: test_root + - object_id: -2100 + display_name: foo + - object_id: -2200 + display_name: bar + - object_id: -3100 + display_name: object1 + - object_id: -3200 + display_name: object2 + - object_id: -3300 + display_name: object3 + - object_id: 1 + display_name: com + - object_id: 2 + display_name: example + - object_id: 3 + display_name: categories + - object_id: 4 + display_name: test + +ccm_core.categories: + - object_id: -2000 + unique_id: test0001 + name: test-root + enabled: true + visible: true + abstract_category: false + category_order: 0 + - object_id: -2100 + unique_id: test0002 + name: foo + parent_category_id: -2000 + enabled: true + visible: true + abstract_category: false + category_order: 0 + - object_id: -2200 + unique_id: test0003 + name: bar + parent_category_id: -2100 + enabled: true + visible: true + abstract_category: false + category_order: 0 + - object_id: 1 + unique_id: com + name: com + parent_category_id: -2000 + enabled: true + visible: true + abstract_category: false + category_order: 1 + - object_id: 2 + unique_id: example + name: example + parent_category_id: 1 + enabled: true + visible: true + abstract_category: false + category_order: 1 + - object_id: 3 + unique_id: categories + name: categories + parent_category_id: 2 + enabled: true + visible: true + abstract_category: false + category_order: 1 + - object_id: 4 + unique_id: test + name: test + parent_category_id: 3 + enabled: true + visible: true + abstract_category: false + category_order: 1 + +ccm_core.category_domains: + - object_id: -1000 + domain_key: test + root_category_id: -2000 + uri: http://libreccm.org/test + version: 1.0 + +ccm_core.categorizations: + - categorization_id: -10000 + category_id: -2100 + object_id: -3100 + object_order: 1 + category_order: 1 + category_index: false + - categorization_id: -10100 + category_id: -2200 + object_id: -3300 + category_order: 1 + object_order: 1 + category_index: false \ No newline at end of file diff --git a/ccm-core/src/test/resources/datasets/org/libreccm/configuration/ConfigurationManagerTest/after-save-new.yml b/ccm-core/src/test/resources/datasets/org/libreccm/configuration/ConfigurationManagerTest/after-save-new.yml index b16c65fea..44f3ba5d1 100644 --- a/ccm-core/src/test/resources/datasets/org/libreccm/configuration/ConfigurationManagerTest/after-save-new.yml +++ b/ccm-core/src/test/resources/datasets/org/libreccm/configuration/ConfigurationManagerTest/after-save-new.yml @@ -21,15 +21,15 @@ ccm_core.ccm_objects: display_name: itemsPerPage - object_id: -3500 display_name: helpUri - - object_id: -2500 + - object_id: 1 display_name: com - - object_id: -2600 + - object_id: 2 display_name: example - - object_id: -2700 + - object_id: 3 display_name: TestConfiguration - - object_id: -3600 + - object_id: 5 display_name: enabled - - object_id: -3700 + - object_id: 7 display_name: itemsPerPage ccm_core.categories: @@ -72,29 +72,26 @@ ccm_core.categories: abstract_category: false parent_category_id: -2300 category_order: 1 - - object_id: -2500 - unique_id: 36223799-5df7-4875-8191-f1ced0965237 + - object_id: 1 name: com enabled: true visible: true abstract_category: false parent_category_id: -2000 category_order: 1 - - object_id: -2600 - unique_id: 22f6f7c6-2ca1-457b-9b3f-185a2c6f39be + - object_id: 2 name: example enabled: true visible: true abstract_category: false - parent_category_id: -2500 + parent_category_id: 1 category_order: 1 - - object_id: -2700 - unique_id: af6c0e93-d60b-4c5f-8fe4-5f82a7f8f923 + - object_id: 3 name: TestConfiguration enabled: true visible: true abstract_category: false - parent_category_id: -2600 + parent_category_id: 2 category_order: 1 ccm_core.category_domains: @@ -104,23 +101,11 @@ ccm_core.category_domains: version: 1.0 ccm_core.categorizations: - - categorization_id: -10100 + - categorization_id: -10500 category_id: -2400 - object_id: -3100 + object_id: -3500 category_order: 1 - object_order: 1 - category_index: false - - categorization_id: -10200 - category_id: -2400 - object_id: -3200 - category_order: 1 - object_order: 2 - category_index: false - - categorization_id: -10300 - category_id: -2400 - object_id: -3300 - category_order: 1 - object_order: 3 + object_order: 5 category_index: false - categorization_id: -10400 category_id: -2400 @@ -128,27 +113,51 @@ ccm_core.categorizations: category_order: 1 object_order: 4 category_index: false - - categorization_id: -10500 + - categorization_id: -10300 category_id: -2400 - object_id: -3500 + object_id: -3300 category_order: 1 - object_order: 5 + object_order: 3 + category_index: false + - categorization_id: -10200 + category_id: -2400 + object_id: -3200 + category_order: 1 + object_order: 2 + category_index: false + - categorization_id: -10100 + category_id: -2400 + object_id: -3100 + category_order: 1 + object_order: 1 + category_index: false + - categorization_id: 4 + category_id: 3 + object_id: 5 + category_order: 1 + object_order: 1 + category_index: false + - categorization_id: 6 + category_id: 3 + object_id: 7 + category_order: 1 + object_order: 2 category_index: false ccm_core.settings: - - object_id: -3100 - name: price - - object_id: -3200 - name: enabled + - object_id: -3500 + name: helpUrl - object_id: -3300 name: minTemperature - object_id: -3400 name: itemsPerPage - - object_id: -3500 - name: helpUrl - - object_id: -3600 + - object_id: -3200 name: enabled - - object_id: -3700 + - object_id: -3100 + name: price + - object_id: 5 + name: enabled + - object_id: 7 name: itemsPerPage ccm_core.settings_big_decimal: @@ -158,7 +167,7 @@ ccm_core.settings_big_decimal: ccm_core.settings_boolean: - object_id: -3200 setting_value: true - - object_id: -3600 + - object_id: 5 setting_value: false ccm_core.settings_double: @@ -168,7 +177,7 @@ ccm_core.settings_double: ccm_core.settings_long: - object_id: -3400 setting_value: 20 - - object_id: -3700 + - object_id: 7 setting_value: 40 ccm_core.settings_string: diff --git a/ccm-core/src/test/resources/scripts/pgsql-cleanup.sql b/ccm-core/src/test/resources/scripts/pgsql-cleanup.sql index ae96b9b81..167412443 100644 --- a/ccm-core/src/test/resources/scripts/pgsql-cleanup.sql +++ b/ccm-core/src/test/resources/scripts/pgsql-cleanup.sql @@ -1,43 +1,43 @@ -DELETE FROM ccm_core.settings_big_decimal; - -DELETE FROM ccm_core.settings_boolean; - -DELETE FROM ccm_core.settings_double; - -DELETE FROM ccm_core.settings_enum_values; - -DELETE FROM ccm_core.settings_enum; - -DELETE FROM ccm_core.settings_l10n_string; - -DELETE FROM ccm_core.settings_l10n_str_values; - -DELETE FROM ccm_core.settings_long; - -DELETE FROM ccm_core.settings_string; - -DELETE FROM ccm_core.settings; - -DELETE FROM ccm_core.categorizations; - -DELETE FROM ccm_core.category_domains; - -DELETE FROM ccm_core.categories; - -DELETE FROM ccm_core.permissions; - -DELETE FROM ccm_core.ccm_objects; - -DELETE FROM ccm_core.role_memberships; - -DELETE FROM ccm_core.group_memberships; - -DELETE FROM ccm_core.groups; - -DELETE FROM ccm_core.users; - -DELETE FROM ccm_core.user_email_addresses; - -DELETE FROM ccm_core.parties; - -DELETE FROM ccm_core.ccm_roles; \ No newline at end of file +-- DELETE FROM ccm_core.settings_big_decimal; +-- +-- DELETE FROM ccm_core.settings_boolean; +-- +-- DELETE FROM ccm_core.settings_double; +-- +-- DELETE FROM ccm_core.settings_enum_values; +-- +-- DELETE FROM ccm_core.settings_enum; +-- +-- DELETE FROM ccm_core.settings_l10n_string; +-- +-- DELETE FROM ccm_core.settings_l10n_str_values; +-- +-- DELETE FROM ccm_core.settings_long; +-- +-- DELETE FROM ccm_core.settings_string; +-- +-- DELETE FROM ccm_core.settings; +-- +-- DELETE FROM ccm_core.categorizations; +-- +-- DELETE FROM ccm_core.category_domains; +-- +-- DELETE FROM ccm_core.categories; +-- +-- DELETE FROM ccm_core.permissions; +-- +-- DELETE FROM ccm_core.ccm_objects; +-- +-- DELETE FROM ccm_core.role_memberships; +-- +-- DELETE FROM ccm_core.group_memberships; +-- +-- DELETE FROM ccm_core.groups; +-- +-- DELETE FROM ccm_core.users; +-- +-- DELETE FROM ccm_core.user_email_addresses; +-- +-- DELETE FROM ccm_core.parties; +-- +-- DELETE FROM ccm_core.ccm_roles; \ No newline at end of file