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 786dac824..52488afb3 100644 --- a/ccm-core/src/main/java/org/libreccm/categorization/CategoryRepository.java +++ b/ccm-core/src/main/java/org/libreccm/categorization/CategoryRepository.java @@ -20,7 +20,6 @@ package org.libreccm.categorization; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.message.FormattedMessage; import org.libreccm.core.AbstractEntityRepository; import java.util.List; diff --git a/ccm-core/src/main/java/org/libreccm/configuration/ApplicationConfigurationManager.java b/ccm-core/src/main/java/org/libreccm/configuration/ApplicationConfigurationManager.java new file mode 100644 index 000000000..1c5f5248a --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/configuration/ApplicationConfigurationManager.java @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2016 LibreCCM Foundation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.libreccm.configuration; + +import static org.libreccm.configuration.ConfigurationConstants.*; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.libreccm.categorization.Category; +import org.libreccm.categorization.CategoryManager; +import org.libreccm.categorization.CategoryRepository; +import org.libreccm.categorization.Domain; +import org.libreccm.categorization.DomainRepository; +import org.libreccm.web.CcmApplication; + +import java.lang.reflect.Field; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; +import javax.persistence.EntityManager; + +/** + * + * @author Jens Pelzetter + */ +@RequestScoped +public class ApplicationConfigurationManager { + + private static final Logger LOGGER = LogManager.getLogger( + ApplicationConfigurationManager.class + ); + + @Inject + private ConfigurationManager confManager; + + @Inject + private SettingManager settingManager; + + @Inject + private SettingConverter settingConverter; + + @Inject + private DomainRepository domainRepo; + + @Inject + private CategoryRepository categoryRepo; + + @Inject + private CategoryManager categoryManager; + + @Inject + private EntityManager entityManager; + + /** + * Finds an application instance specific configuration and loads it values + * from the registry. + * + * @param The type of the configuration. + * @param confClass The configuration class. + * @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) { + if (confClass == null) { + throw new IllegalArgumentException("confClass can't be null"); + } + + if (instance == null) { + throw new IllegalArgumentException("instance can't be null"); + } + + if (confClass.getAnnotation(Configuration.class) == null) { + throw new IllegalArgumentException(String.format( + "Provided class \"%s\" is not annotated with \"%s\".", + confClass.getName(), + Configuration.class.getName())); + } + + final String confName = String.format("%s.%s", + confClass.getName(), + instance.getPrimaryUrl()); + + return confManager.findConfiguration(confName, confClass); + } + + /** + * Saves a application instance configuration. + * + * @param configuration The configuration to save. + * @param instance The application instance of which the configuration + * stores the settings. + */ + public void saveConfiguration(final ApplicationConfiguration configuration, + final CcmApplication instance) { + if (configuration == null) { + throw new IllegalArgumentException("Configuration can't be null"); + } + + 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())); + } + + if (instance == null) { + throw new IllegalArgumentException("Instance can't be null"); + } + + final Field[] fields = configuration.getClass().getDeclaredFields(); + for (final Field field : fields) { + field.setAccessible(true); + try { + setSettingValue(configuration, + instance, + confManager.getSettingName(field), + field.getType(), + field.get(configuration)); + } catch (IllegalAccessException ex) { + LOGGER.error(String.format( + "Failed to write setting value for setting \"%s\" " + + "of configuration \"%s\"", + confManager.getSettingName(field), + configuration.getClass().getName()), + ex); + throw new IllegalStateException(String.format( + "Failed to write setting value for setting \"%s\" " + + "of configuration \"%s\"", + confManager.getSettingName(field), + configuration.getClass().getName()), + ex); + } + } + } + + /** + * Sets the value of a setting of application instance configuration. + * + * @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. + */ + private void setSettingValue(final Object configuration, + final CcmApplication instance, + final String settingName, + final Class valueType, + final Object value) { + final String settingPath = String.format( + "%s.%s.%s", + configuration.getClass().getName(), + instance.getPrimaryUrl(), + settingName); + AbstractSetting setting = settingManager.findSetting(settingPath, valueType); + if (setting == null) { + setting = settingConverter.createSettingForValueType(valueType); + setting.setName(settingName); + final Domain registry = domainRepo + .findByDomainKey(REGISTRY_DOMAIN); + final Category category = categoryRepo + .findByPath(registry, configuration.getClass().getName()); + categoryManager.addObjectToCategory(setting, category); + } + + @SuppressWarnings("unchecked") + final T settingValue = (T) value; + setting.setValue(settingValue); + + entityManager.merge(setting); + } + +} 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 62e99f4cc..65a95d206 100644 --- a/ccm-core/src/main/java/org/libreccm/configuration/ConfigurationManager.java +++ b/ccm-core/src/main/java/org/libreccm/configuration/ConfigurationManager.java @@ -23,22 +23,13 @@ import static org.libreccm.configuration.ConfigurationConstants.*; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.FormattedMessage; -import org.libreccm.categorization.Categorization; import org.libreccm.categorization.Category; import org.libreccm.categorization.CategoryManager; import org.libreccm.categorization.CategoryRepository; import org.libreccm.categorization.Domain; import org.libreccm.categorization.DomainRepository; -import org.libreccm.core.CcmObject; -import org.libreccm.l10n.LocalizedString; -import org.libreccm.web.CcmApplication; import java.lang.reflect.Field; -import java.math.BigDecimal; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import java.util.Set; import java.util.StringJoiner; import javax.enterprise.context.RequestScoped; @@ -51,24 +42,25 @@ import javax.persistence.EntityManager; * @author Jens Pelzetter */ @RequestScoped -@SuppressWarnings({"PMD.GodClass", - "PMD.TooManyMethods", - "PMD.LongVariable", - "PMD.ModifiedCyclomaticComplexity", - "PMD.StandardCyclomaticComplexity",}) public class ConfigurationManager { private static final Logger LOGGER = LogManager.getLogger( ConfigurationManager.class); + @Inject + private SettingManager settingManager; + + @Inject + private SettingConverter settingConverter; + @Inject private CategoryManager categoryManager; @Inject - private CategoryRepository categoryRepository; + private CategoryRepository categoryRepo; @Inject - private DomainRepository domainRepository; + private DomainRepository domainRepo; @Inject private EntityManager entityManager; @@ -162,92 +154,6 @@ public class ConfigurationManager { } } - /** - * Finds an application instance specific configuration and loads it values - * from the registry. - * - * @param The type of the configuration. - * @param confClass The configuration class. - * @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) { - if (confClass == null) { - throw new IllegalArgumentException("confClass can't be null"); - } - - if (instance == null) { - throw new IllegalArgumentException("instance can't be null"); - } - - if (confClass.getAnnotation(Configuration.class) == null) { - throw new IllegalArgumentException(String.format( - "Provided class \"%s\" is not annotated with \"%s\".", - confClass.getName(), - Configuration.class.getName())); - } - - final String confName = String.format("%s.%s", - confClass.getName(), - instance.getPrimaryUrl()); - - return findConfiguration(confName, confClass); - } - - /** - * Saves a application instance configuration. - * - * @param configuration The configuration to save. - * @param instance The application instance of which the configuration - * stores the settings. - */ - public void saveConfiguration(final ApplicationConfiguration configuration, - final CcmApplication instance) { - if (configuration == null) { - throw new IllegalArgumentException("Configuration can't be null"); - } - - 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())); - } - - if (instance == null) { - throw new IllegalArgumentException("Instance can't be null"); - } - - final Field[] fields = configuration.getClass().getDeclaredFields(); - for (final Field field : fields) { - field.setAccessible(true); - try { - setSettingValue(configuration, - instance, - getSettingName(field), - field.getType(), - 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()), - ex); - throw new IllegalStateException(String.format( - "Failed to write setting value for setting \"%s\" " - + "of configuration \"%s\"", - getSettingName(field), - configuration.getClass().getName()), - ex); - } - } - } - /** * Get the {@link ConfigurationInfo} for a configuration. * @@ -294,205 +200,14 @@ public class ConfigurationManager { for (final Field field : fields) { field.setAccessible(true); if (field.getAnnotation(Setting.class) != null) { - confInfo.addSetting(getSettingInfo(configuration, - field.getName())); + confInfo.addSetting(settingManager.getSettingInfo( + configuration, field.getName())); } } return confInfo; } - /** - * 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. - * - * @return The {@link SettingInfo} for the provided configuration class. - */ - @SuppressWarnings({"PMD.NPathComplexity", - "PMD.CyclomaticComplexity", - "PMD.StandardCyclomaticComplexity"}) - public SettingInfo getSettingInfo(final Class configuration, - final String name) { - if (configuration == null) { - throw new IllegalArgumentException("Configuration can't be null"); - } - - 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())); - } - - final Configuration confAnnotation = configuration.getAnnotation( - Configuration.class); - final String descBundle; - if (confAnnotation.descBundle() == null - || confAnnotation.descBundle().isEmpty()) { - descBundle = String.join("", - configuration.getClass().getName(), - "Description"); - } else { - descBundle = confAnnotation.descBundle(); - } - - final Field field; - try { - 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), - ex); - return null; - } - - if (field.getAnnotation(Setting.class) == null) { - return null; - } - - final Setting settingAnnotation = field.getAnnotation(Setting.class); - final SettingInfo settingInfo = new SettingInfo(); - if (settingAnnotation.name() == null - || settingAnnotation.name().isEmpty()) { - settingInfo.setName(field.getName()); - } else { - settingInfo.setName(settingAnnotation.name()); - } - - settingInfo.setValueType(field.getType().getName()); - - try { - final Object conf = configuration.newInstance(); - settingInfo.setDefaultValue(field.get(conf).toString()); - } catch (InstantiationException | IllegalAccessException ex) { - LOGGER.warn(String.format("Failed to create instance of \"%s\" to " - + "get default values.", - configuration.getName()), - ex); - } - - settingInfo.setConfClass(configuration.getName()); - - settingInfo.setDescBundle(descBundle); - - if (settingAnnotation.labelKey() == null - || settingAnnotation.labelKey().isEmpty()) { - settingInfo.setLabelKey(String.join(".", field.getName(), - "label")); - } else { - settingInfo.setLabelKey(name); - } - - if (settingAnnotation.descKey() == null - || settingAnnotation.descKey().isEmpty()) { - settingInfo.setDescKey(String.join(".", - field.getName(), - "descripotion")); - } else { - settingInfo.setDescKey(settingAnnotation.descKey()); - } - - return settingInfo; - } - - /** - * 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 clazz The class of the setting. - * - * @return The requested setting if it exists in the registry, {@code null} - * 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())); - final String[] tokens = name.split("\\."); - LOGGER.debug(String.format("Setting name \"%s\" has %d tokens.", - name, - tokens.length)); - final String[] categoryTokens = Arrays.copyOfRange(tokens, - 0, - tokens.length - 1); - final String categoryPath = String.join(".", categoryTokens); - LOGGER.debug(String.format("categoryPath for setting is \"%s\".", - categoryPath)); - - final Domain registry = domainRepository - .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))); - 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(); - - if (result.isPresent()) { - final CcmObject object = result.get().getCategorizedObject(); - final AbstractSetting entry = (AbstractSetting) object; - - if (clazz.isInstance(entry.getValue())) { - @SuppressWarnings("unchecked") - final AbstractSetting resultEntry - = (AbstractSetting) entry; - return resultEntry; - } else { - LOGGER.warn(String.format("Setting \"%s\" found but is not of " - + "the requested type \"%s\".", - name, - clazz.getName())); - return null; - } - } else { - LOGGER.warn(String.format( - "Setting \"%s\" was not found in category \"%s\".", - name, - categoryPath)); - return null; - } - } - - /** - * Low level method of saving a setting. - * - * @param setting The setting to save. - */ - public void saveSetting(final AbstractSetting setting) { - if (setting.getObjectId() == 0) { - entityManager.persist(setting); - } else { - entityManager.merge(setting); - } - } - /** * Helper method for generating the name of a setting. This method does not * check if the provided field is annotated with {@link Setting}. The caller @@ -504,7 +219,7 @@ public class ConfigurationManager { * @return The name of the field or if the {@link Setting} annotation of the * field has a name value, the value of that field. */ - private String getSettingName(final Field field) { + String getSettingName(final Field field) { LOGGER.debug(String.format("Trying to get setting name from field: " + "\"%s\"", field.getName())); @@ -528,32 +243,32 @@ public class ConfigurationManager { * @throws IllegalArgumentException If there is not setting type for the * provided value type. */ - @SuppressWarnings("unchecked") - private AbstractSetting createSettingForValueType( - final Class valueType) { - - final String valueTypeName = valueType.getName(); - if (BigDecimal.class.getName().equals(valueTypeName)) { - return (AbstractSetting) new BigDecimalSetting(); - } else if (Boolean.class.getName().equals(valueTypeName)) { - return (AbstractSetting) new BooleanSetting(); - } else if (Double.class.getName().equals(valueTypeName)) { - return (AbstractSetting) new DoubleSetting(); - } else if (List.class.getName().equals(valueTypeName)) { - return (AbstractSetting) new StringListSetting(); - } else if (LocalizedString.class.getName().equals(valueTypeName)) { - return (AbstractSetting) new LocalizedStringSetting(); - } else if (Long.class.getName().equals(valueTypeName)) { - return (AbstractSetting) new LongSetting(); - } else if (Set.class.getName().equals(valueTypeName)) { - return (AbstractSetting) new EnumSetting(); - } 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)); - } - } +// @SuppressWarnings("unchecked") +// AbstractSetting createSettingForValueType( +// final Class valueType) { +// +// final String valueTypeName = valueType.getName(); +// if (BigDecimal.class.getName().equals(valueTypeName)) { +// return (AbstractSetting) new BigDecimalSetting(); +// } else if (Boolean.class.getName().equals(valueTypeName)) { +// return (AbstractSetting) new BooleanSetting(); +// } else if (Double.class.getName().equals(valueTypeName)) { +// return (AbstractSetting) new DoubleSetting(); +// } else if (List.class.getName().equals(valueTypeName)) { +// return (AbstractSetting) new StringListSetting(); +// } else if (LocalizedString.class.getName().equals(valueTypeName)) { +// return (AbstractSetting) new LocalizedStringSetting(); +// } else if (Long.class.getName().equals(valueTypeName)) { +// return (AbstractSetting) new LongSetting(); +// } else if (Set.class.getName().equals(valueTypeName)) { +// return (AbstractSetting) new EnumSetting(); +// } 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)); +// } +// } /** * Sets a value on a setting in the registry. @@ -576,12 +291,13 @@ public class ConfigurationManager { "Saving setting \"%s\" of type \"%s\"...", settingPath, valueType.getName())); - AbstractSetting setting = findSetting(settingPath, valueType); + AbstractSetting setting = settingManager.findSetting(settingPath, + valueType); if (setting == null) { LOGGER.debug(String.format("Setting \"%s\" does not yet exist in " + "database. Creating new setting.", settingPath)); - setting = createSettingForValueType(valueType); + setting = settingConverter.createSettingForValueType(valueType); setting.setName(settingName); setting.setDisplayName(settingName); final Category category = findCategoryForNewSetting(configuration); @@ -603,44 +319,6 @@ public class ConfigurationManager { entityManager.flush(); } - /** - * Sets the value of a setting of application instance configuration. - * - * @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. - */ - private void setSettingValue(final Object configuration, - final CcmApplication instance, - final String settingName, - final Class valueType, - final Object value) { - final String settingPath = String.format( - "%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); - final Category category = categoryRepository - .findByPath(registry, configuration.getClass().getName()); - categoryManager.addObjectToCategory(setting, category); - } - - @SuppressWarnings("unchecked") - final T settingValue = (T) value; - setting.setValue(settingValue); - - entityManager.merge(setting); - } - /** * Helper method for loading a configuration from the registry. * @@ -657,13 +335,8 @@ public class ConfigurationManager { * @return An instance of the configuration class with all setting fields * set to the values stored in the registry. */ - private T findConfiguration(final String confName, - final Class confClass) { - final Domain registry = domainRepository - .findByDomainKey(REGISTRY_DOMAIN); - final Category category = categoryRepository.findByPath(registry, - confName); - + T findConfiguration(final String confName, + final Class confClass) { final T conf; try { conf = confClass.newInstance(); @@ -675,7 +348,9 @@ public class ConfigurationManager { return null; } - if (category == null) { + final Domain registry = domainRepo + .findByDomainKey(REGISTRY_DOMAIN); + if (categoryRepo.findByPath(registry, confName) == null) { return conf; } @@ -690,8 +365,8 @@ public class ConfigurationManager { confClass.getName(), getSettingName(field)); final Class settingType = field.getType(); - final AbstractSetting setting = findSetting(settingPath, - settingType); + final AbstractSetting setting = settingManager.findSetting( + settingPath, settingType); if (setting != null) { try { LOGGER.debug("Setting \"{}\" found. Value: %s", @@ -717,7 +392,7 @@ public class ConfigurationManager { configuration.getClass().getName()); final String categoryPath = configuration.getClass().getName(); final String[] tokens = categoryPath.split("\\."); - final Domain registry = domainRepository + final Domain registry = domainRepo .findByDomainKey(REGISTRY_DOMAIN); final Category[] categories = new Category[tokens.length]; @@ -729,8 +404,8 @@ public class ConfigurationManager { LOGGER.debug("#findCategoryForNewSetting: " + "Checking if category \"{}\" exists.", path); - final Category category = categoryRepository.findByPath(registry, - path); + final Category category = categoryRepo.findByPath(registry, + path); if (category == null) { LOGGER.debug("#findCategoryForNewSetting: " + "Category \"{}\" does not exist.", @@ -778,7 +453,7 @@ public class ConfigurationManager { categoryPath); return categories[categories.length - 1]; } - + private String buildCategoryPath(final String[] tokens, final int index) { final StringJoiner joiner = new StringJoiner("."); @@ -794,7 +469,7 @@ public class ConfigurationManager { final Category category = new Category(); category.setName(name); category.setDisplayName(name); - categoryRepository.save(category); + categoryRepo.save(category); entityManager.flush(); categoryManager.addSubCategoryToCategory(category, parent); diff --git a/ccm-core/src/main/java/org/libreccm/configuration/SettingConverter.java b/ccm-core/src/main/java/org/libreccm/configuration/SettingConverter.java new file mode 100644 index 000000000..7baa7f601 --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/configuration/SettingConverter.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2016 LibreCCM Foundation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.libreccm.configuration; + +import org.libreccm.l10n.LocalizedString; + +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.enterprise.context.RequestScoped; + +/** + * Helper class for converting values to settings. + * + * @author Jens Pelzetter + */ +@RequestScoped +class SettingConverter { + + private final Map>> typeMap; + + public SettingConverter() { + typeMap = new HashMap<>(); + typeMap.put(BigDecimal.class.getName(), BigDecimalSetting.class); + typeMap.put(Boolean.class.getName(), BooleanSetting.class); + typeMap.put(Double.class.getName(), DoubleSetting.class); + typeMap.put(List.class.getName(), StringListSetting.class); + typeMap.put(LocalizedString.class.getName(), + LocalizedStringSetting.class); + typeMap.put(Long.class.getName(), LongSetting.class); + typeMap.put(Set.class.getName(), EnumSetting.class); + typeMap.put(String.class.getName(), StringSetting.class); + } + + /** + * Create a setting instance of a specific value type. + * + * @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. + */ + @SuppressWarnings("unchecked") + AbstractSetting createSettingForValueType(final Class valueType) { + + final String valueTypeName = valueType.getName(); + final Class> clazz = typeMap.get( + valueTypeName); + + if (clazz == null) { + throw new IllegalArgumentException(String.format( + "No setting type for value type \"%s\".", valueTypeName)); + } else { + try { + return (AbstractSetting) clazz.newInstance(); + } catch (InstantiationException | IllegalAccessException ex) { + throw new IllegalStateException( + "Failed to create setting instance.", ex); + } + } + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/configuration/SettingManager.java b/ccm-core/src/main/java/org/libreccm/configuration/SettingManager.java new file mode 100644 index 000000000..edaeb886a --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/configuration/SettingManager.java @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2016 LibreCCM Foundation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.libreccm.configuration; + +import static org.libreccm.configuration.ConfigurationConstants.*; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.libreccm.categorization.Categorization; +import org.libreccm.categorization.Category; +import org.libreccm.categorization.CategoryRepository; +import org.libreccm.categorization.Domain; +import org.libreccm.categorization.DomainRepository; +import org.libreccm.core.CcmObject; + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.Optional; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; +import javax.persistence.EntityManager; + +/** + * Manages settings in the registry. Normally there should be no need to use + * this class directly because the {@link ConfigurationManager} provide the same + * public methods for accessing settings than this class. The purpose of this + * class is only to separate the logic for managing settings from the logic for + * managing configuration classes and to reduce the complexity of the + * {@link ConfigurationManager} class + * + * @author Jens Pelzetter + */ +@RequestScoped +public class SettingManager { + + private static final Logger LOGGER = LogManager.getLogger( + SettingManager.class); + + @Inject + private CategoryRepository categoryRepo; + + @Inject + private DomainRepository domainRepo; + + @Inject + private EntityManager entityManager; + + /** + * 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. + * + * @return The {@link SettingInfo} for the provided configuration class. + */ + @SuppressWarnings({"PMD.NPathComplexity", + "PMD.CyclomaticComplexity", + "PMD.StandardCyclomaticComplexity"}) + public SettingInfo getSettingInfo(final Class configuration, + final String name) { + if (configuration == null) { + throw new IllegalArgumentException("Configuration can't be null"); + } + + 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())); + } + +// final Configuration confAnnotation = configuration.getAnnotation( +// Configuration.class); +// final String descBundle; +// if (confAnnotation.descBundle() == null +// || confAnnotation.descBundle().isEmpty()) { +// descBundle = String.join("", +// configuration.getClass().getName(), +// "Description"); +// } else { +// descBundle = confAnnotation.descBundle(); +// } + + + + final Field field; + try { + 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), + ex); + return null; + } + + if (field.getAnnotation(Setting.class) == null) { + return null; + } + + final Setting settingAnnotation = field.getAnnotation(Setting.class); + final SettingInfo settingInfo = new SettingInfo(); + if (settingAnnotation.name() == null + || settingAnnotation.name().isEmpty()) { + settingInfo.setName(field.getName()); + } else { + settingInfo.setName(settingAnnotation.name()); + } + + settingInfo.setValueType(field.getType().getName()); + + try { + final Object conf = configuration.newInstance(); + settingInfo.setDefaultValue(field.get(conf).toString()); + } catch (InstantiationException | IllegalAccessException ex) { + LOGGER.warn(String.format("Failed to create instance of \"%s\" to " + + "get default values.", + configuration.getName()), + ex); + } + + settingInfo.setConfClass(configuration.getName()); + settingInfo.setDescBundle(getDescBundle(configuration)); + + if (settingAnnotation.labelKey() == null + || settingAnnotation.labelKey().isEmpty()) { + settingInfo.setLabelKey(String.join(".", field.getName(), + "label")); + } else { + settingInfo.setLabelKey(name); + } + + if (settingAnnotation.descKey() == null + || settingAnnotation.descKey().isEmpty()) { + settingInfo.setDescKey(String.join(".", + field.getName(), + "descripotion")); + } else { + settingInfo.setDescKey(settingAnnotation.descKey()); + } + + return settingInfo; + } + + /** + * 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 clazz The class of the setting. + * + * @return The requested setting if it exists in the registry, {@code null} + * 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())); + final String[] tokens = name.split("\\."); + LOGGER.debug(String.format("Setting name \"%s\" has %d tokens.", + name, + tokens.length)); + final String[] categoryTokens = Arrays.copyOfRange(tokens, + 0, + tokens.length - 1); + final String categoryPath = String.join(".", categoryTokens); + LOGGER.debug(String.format("categoryPath for setting is \"%s\".", + categoryPath)); + + final Domain registry = domainRepo + .findByDomainKey(REGISTRY_DOMAIN); + final Category category = categoryRepo.findByPath(registry, + categoryPath); + if (category == null) { + LOGGER.warn(String.format(String.format( + "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(); + + if (result.isPresent()) { + final CcmObject object = result.get().getCategorizedObject(); + final AbstractSetting entry = (AbstractSetting) object; + + if (clazz.isInstance(entry.getValue())) { + @SuppressWarnings("unchecked") + final AbstractSetting resultEntry + = (AbstractSetting) entry; + return resultEntry; + } else { + LOGGER.warn(String.format("Setting \"%s\" found but is not of " + + "the requested type \"%s\".", + name, + clazz.getName())); + return null; + } + } else { + LOGGER.warn(String.format( + "Setting \"%s\" was not found in category \"%s\".", + name, + categoryPath)); + return null; + } + } + + + + /** + * Low level method of saving a setting. + * + * @param setting The setting to save. + */ + public void saveSetting(final AbstractSetting setting) { + if (setting.getObjectId() == 0) { + entityManager.persist(setting); + } else { + entityManager.merge(setting); + } + } + + private String getDescBundle(final Class configuration) { + final Configuration confAnnotation = configuration.getAnnotation( + Configuration.class); + if (confAnnotation.descBundle() == null + || confAnnotation.descBundle().isEmpty()) { + return String.join("", + configuration.getClass().getName(), + "Description"); + } else { + return confAnnotation.descBundle(); + } + } + +}