CCM NG: Split class ConfigurationManager into smaller classes to reduce complexity.

git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@3825 8810af33-2d31-482b-a856-94f89814c4df
pull/2/head
jensp 2016-01-24 15:49:19 +00:00
parent cc7889143c
commit 73eef4aeb0
5 changed files with 603 additions and 378 deletions

View File

@ -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;

View File

@ -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 <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@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 <T> 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 extends ApplicationConfiguration> T findConfiguration(
final Class<T> 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 <T> 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 <T> void setSettingValue(final Object configuration,
final CcmApplication instance,
final String settingName,
final Class<T> valueType,
final Object value) {
final String settingPath = String.format(
"%s.%s.%s",
configuration.getClass().getName(),
instance.getPrimaryUrl(),
settingName);
AbstractSetting<T> 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);
}
}

View File

@ -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 <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@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 <T> 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 extends ApplicationConfiguration> T findConfiguration(
final Class<T> 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 <T> 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 <T> AbstractSetting<T> findSetting(final String name,
final Class<T> 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<Categorization> 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<T> resultEntry
= (AbstractSetting<T>) 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 <T> AbstractSetting<T> createSettingForValueType(
final Class<T> valueType) {
final String valueTypeName = valueType.getName();
if (BigDecimal.class.getName().equals(valueTypeName)) {
return (AbstractSetting<T>) new BigDecimalSetting();
} else if (Boolean.class.getName().equals(valueTypeName)) {
return (AbstractSetting<T>) new BooleanSetting();
} else if (Double.class.getName().equals(valueTypeName)) {
return (AbstractSetting<T>) new DoubleSetting();
} else if (List.class.getName().equals(valueTypeName)) {
return (AbstractSetting<T>) new StringListSetting();
} else if (LocalizedString.class.getName().equals(valueTypeName)) {
return (AbstractSetting<T>) new LocalizedStringSetting();
} else if (Long.class.getName().equals(valueTypeName)) {
return (AbstractSetting<T>) new LongSetting();
} else if (Set.class.getName().equals(valueTypeName)) {
return (AbstractSetting<T>) new EnumSetting();
} else if (String.class.getName().equals(valueTypeName)) {
return (AbstractSetting<T>) new StringSetting();
} else {
throw new IllegalArgumentException(String.format(
"No setting type for value type \"%s\".", valueTypeName));
}
}
// @SuppressWarnings("unchecked")
// <T> AbstractSetting<T> createSettingForValueType(
// final Class<T> valueType) {
//
// final String valueTypeName = valueType.getName();
// if (BigDecimal.class.getName().equals(valueTypeName)) {
// return (AbstractSetting<T>) new BigDecimalSetting();
// } else if (Boolean.class.getName().equals(valueTypeName)) {
// return (AbstractSetting<T>) new BooleanSetting();
// } else if (Double.class.getName().equals(valueTypeName)) {
// return (AbstractSetting<T>) new DoubleSetting();
// } else if (List.class.getName().equals(valueTypeName)) {
// return (AbstractSetting<T>) new StringListSetting();
// } else if (LocalizedString.class.getName().equals(valueTypeName)) {
// return (AbstractSetting<T>) new LocalizedStringSetting();
// } else if (Long.class.getName().equals(valueTypeName)) {
// return (AbstractSetting<T>) new LongSetting();
// } else if (Set.class.getName().equals(valueTypeName)) {
// return (AbstractSetting<T>) new EnumSetting();
// } else if (String.class.getName().equals(valueTypeName)) {
// return (AbstractSetting<T>) 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<T> setting = findSetting(settingPath, valueType);
AbstractSetting<T> 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 <T> 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 <T> void setSettingValue(final Object configuration,
final CcmApplication instance,
final String settingName,
final Class<T> valueType,
final Object value) {
final String settingPath = String.format(
"%s.%s.%s",
configuration.getClass().getName(),
instance.getPrimaryUrl(),
settingName);
AbstractSetting<T> 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> T findConfiguration(final String confName,
final Class<T> confClass) {
final Domain registry = domainRepository
.findByDomainKey(REGISTRY_DOMAIN);
final Category category = categoryRepository.findByPath(registry,
confName);
<T> T findConfiguration(final String confName,
final Class<T> 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.",
@ -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);

View File

@ -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 <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
class SettingConverter {
private final Map<String, Class<? extends AbstractSetting<?>>> 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 <T> 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")
<T> AbstractSetting<T> createSettingForValueType(final Class<T> valueType) {
final String valueTypeName = valueType.getName();
final Class<? extends AbstractSetting<?>> clazz = typeMap.get(
valueTypeName);
if (clazz == null) {
throw new IllegalArgumentException(String.format(
"No setting type for value type \"%s\".", valueTypeName));
} else {
try {
return (AbstractSetting<T>) clazz.newInstance();
} catch (InstantiationException | IllegalAccessException ex) {
throw new IllegalStateException(
"Failed to create setting instance.", ex);
}
}
}
}

View File

@ -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 <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@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 <T> 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 <T> AbstractSetting<T> findSetting(final String name,
final Class<T> 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<Categorization> 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<T> resultEntry
= (AbstractSetting<T>) 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();
}
}
}