From fd010fef1a419cfba1de5cb87effdfa0b800b43c Mon Sep 17 00:00:00 2001 From: jensp Date: Wed, 16 Dec 2015 15:15:38 +0000 Subject: [PATCH] CCM NG: Classes for the new database based configuration system (not tested yet) git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@3772 8810af33-2d31-482b-a856-94f89814c4df --- ...urationEntry.java => AbstractSetting.java} | 54 +- .../ApplicationConfiguration.java | 133 ++++ ...ationEntry.java => BigDecimalSetting.java} | 32 +- ...gurationEntry.java => BooleanSetting.java} | 15 +- .../libreccm/configuration/Configuration.java | 2 +- .../configuration/ConfigurationConstants.java | 3 +- .../configuration/ConfigurationInfo.java | 148 +++++ .../configuration/ConfigurationManager.java | 586 +++++++++++++++++- ...igurationEntry.java => DoubleSetting.java} | 15 +- ...nfigurationEntry.java => EnumSetting.java} | 16 +- ...Entry.java => LocalizedStringSetting.java} | 19 +- ...nfigurationEntry.java => LongSetting.java} | 17 +- .../libreccm/configuration/SettingInfo.java | 179 ++++++ ...igurationEntry.java => StringSetting.java} | 15 +- .../libreccm/configuration/package-info.java | 55 ++ .../java/org/libreccm/web/CcmApplication.java | 2 - .../ccm_core/h2/V7_0_0_0__create_tables.sql | 176 +++--- .../pgsql/V7_0_0_0__create_tables.sql | 176 +++--- .../configuration/EqualsAndHashCodeTest.java | 14 +- .../libreccm/configuration/ToStringTest.java | 14 +- .../scripts/create_ccm_core_schema.sql | 177 +++--- .../scripts/create_ccm_core_schema.sql | 176 +++--- .../libreccm/shortcuts/ui/ShortcutForm.java | 69 +-- .../libreccm/shortcuts/ui/ShortcutsTable.java | 31 +- 24 files changed, 1610 insertions(+), 514 deletions(-) rename ccm-core/src/main/java/org/libreccm/configuration/{AbstractConfigurationEntry.java => AbstractSetting.java} (61%) create mode 100644 ccm-core/src/main/java/org/libreccm/configuration/ApplicationConfiguration.java rename ccm-core/src/main/java/org/libreccm/configuration/{BigDecimalConfigurationEntry.java => BigDecimalSetting.java} (81%) rename ccm-core/src/main/java/org/libreccm/configuration/{BooleanConfigurationEntry.java => BooleanSetting.java} (86%) create mode 100644 ccm-core/src/main/java/org/libreccm/configuration/ConfigurationInfo.java rename ccm-core/src/main/java/org/libreccm/configuration/{DoubleConfigurationEntry.java => DoubleSetting.java} (86%) rename ccm-core/src/main/java/org/libreccm/configuration/{EnumConfigurationEntry.java => EnumSetting.java} (88%) rename ccm-core/src/main/java/org/libreccm/configuration/{LocalizedStringConfigurationEntry.java => LocalizedStringSetting.java} (84%) rename ccm-core/src/main/java/org/libreccm/configuration/{LongConfigurationEntry.java => LongSetting.java} (85%) create mode 100644 ccm-core/src/main/java/org/libreccm/configuration/SettingInfo.java rename ccm-core/src/main/java/org/libreccm/configuration/{StringConfigurationEntry.java => StringSetting.java} (86%) create mode 100644 ccm-core/src/main/java/org/libreccm/configuration/package-info.java diff --git a/ccm-core/src/main/java/org/libreccm/configuration/AbstractConfigurationEntry.java b/ccm-core/src/main/java/org/libreccm/configuration/AbstractSetting.java similarity index 61% rename from ccm-core/src/main/java/org/libreccm/configuration/AbstractConfigurationEntry.java rename to ccm-core/src/main/java/org/libreccm/configuration/AbstractSetting.java index b9e51e243..ff662d802 100644 --- a/ccm-core/src/main/java/org/libreccm/configuration/AbstractConfigurationEntry.java +++ b/ccm-core/src/main/java/org/libreccm/configuration/AbstractSetting.java @@ -20,6 +20,7 @@ package org.libreccm.configuration; import static org.libreccm.core.CoreConstants.*; +import org.hibernate.validator.constraints.NotBlank; import org.libreccm.core.CcmObject; import java.io.Serializable; @@ -28,38 +29,55 @@ import java.util.Objects; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; +import javax.validation.constraints.Pattern; /** - * + * Abstract base class for all settings. + * * @author Jens Pelzetter - * @param + * @param The value type of the setting. */ @Entity -@Table(name = "CONFIGURATION_ENTRIES", schema = DB_SCHEMA) -public abstract class AbstractConfigurationEntry +@Table(name = "SETTINGS", schema = DB_SCHEMA) +public abstract class AbstractSetting extends CcmObject implements Serializable { private static final long serialVersionUID = -839223659103128135L; - @Column(name = "comment", length = 2048) - private String comment; + /** + * The name of the setting. The string must be a valid URL fragment. + */ + @Column(name = "name", nullable = false, length = 512) + @NotBlank + @Pattern(regexp = "[\\w-.]*") + private String name; - public String getComment() { - return comment; + public String getName() { + return name; } - public void setComment(final String comment) { - this.comment = comment; + public void setName(final String name) { + this.name = name; } + /** + * Getter for the value of the setting. + * + * @return The value of the setting. + */ public abstract T getValue(); + /** + * Setter for the value of the setting. + * + * @param value The new value of the setting. + */ public abstract void setValue(T value); @Override public int hashCode() { int hash = super.hashCode(); - hash = 47 * hash + Objects.hashCode(comment); + hash = 47 * hash + Objects.hashCode(name); return hash; } @@ -73,28 +91,28 @@ public abstract class AbstractConfigurationEntry return false; } - if (!(obj instanceof AbstractConfigurationEntry)) { + if (!(obj instanceof AbstractSetting)) { return false; } - final AbstractConfigurationEntry other - = (AbstractConfigurationEntry) obj; + final AbstractSetting other + = (AbstractSetting) obj; if (!other.canEqual(this)) { return false; } - return Objects.equals(comment, other.getComment()); + return Objects.equals(name, other.getName()); } @Override public boolean canEqual(final Object obj) { - return obj instanceof AbstractConfigurationEntry; + return obj instanceof AbstractSetting; } @Override public String toString(final String data) { - return super.toString(String.format(", comment = \"%s\"%s", - comment, + return super.toString(String.format(", name = \"%s\"%s", + name, data)); } diff --git a/ccm-core/src/main/java/org/libreccm/configuration/ApplicationConfiguration.java b/ccm-core/src/main/java/org/libreccm/configuration/ApplicationConfiguration.java new file mode 100644 index 000000000..3b3918ae2 --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/configuration/ApplicationConfiguration.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2015 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.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.libreccm.web.CcmApplication; + +import java.util.Objects; + +/** + * Base class for application instance specific configurations. + * + * @author Jens Pelzetter + */ +public class ApplicationConfiguration { + + private static final Logger LOGGER = LogManager.getLogger( + ApplicationConfiguration.class); + + /** + * The primary URL identifying the application instance for which this + * configuration stores settings. + */ + @Setting + private String applicationInstance; + + /** + * The fully qualified name of the application class. + */ + @Setting + private String applicationClass; + + public String getApplicationInstance() { + return applicationInstance; + } + + public void setApplicationInstance(final String applicationInstance) { + this.applicationInstance = applicationInstance; + } + + public Class getApplicationClass() { + try { + @SuppressWarnings("unchecked") + final Class clazz = (Class) Class + .forName(applicationClass); + return clazz; + } catch (ClassNotFoundException ex) { + LOGGER.warn(String.format( + "Class '%s' for ApplicationConfiguration was not found.", + applicationClass), + ex); + return null; + } + } + + public void setApplicationClass(final Class clazz) { + applicationClass = clazz.getName(); + } + + @Override + public int hashCode() { + int hash = 7; + hash = 79 * hash + Objects.hashCode(applicationInstance); + hash = 79 * hash + Objects.hashCode(applicationClass); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj == null) { + return false; + } + if (!(obj instanceof ApplicationConfiguration)) { + return false; + } + + final ApplicationConfiguration other = (ApplicationConfiguration) obj; + if (!other.canEqual(this)) { + return false; + } + + if (!Objects.equals(applicationInstance, other.getApplicationInstance())) { + return false; + } + if (Objects.equals(applicationClass, other.getApplicationClass())) { + } else { + return false; + } + return true; + } + + public boolean canEqual(final Object obj) { + return obj instanceof ApplicationConfiguration; + } + + @Override + public final String toString() { + return toString(""); + } + + public String toString(final String data) { + return String.format("%s{ " + + "applicationInstance = \"%s\", " + + "applicationClass = \"%s\"%s" + + " }", + super.toString(), + applicationInstance, + applicationClass, + data); + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/configuration/BigDecimalConfigurationEntry.java b/ccm-core/src/main/java/org/libreccm/configuration/BigDecimalSetting.java similarity index 81% rename from ccm-core/src/main/java/org/libreccm/configuration/BigDecimalConfigurationEntry.java rename to ccm-core/src/main/java/org/libreccm/configuration/BigDecimalSetting.java index ae5bef427..622ccf4d6 100644 --- a/ccm-core/src/main/java/org/libreccm/configuration/BigDecimalConfigurationEntry.java +++ b/ccm-core/src/main/java/org/libreccm/configuration/BigDecimalSetting.java @@ -29,24 +29,27 @@ import javax.persistence.Entity; import javax.persistence.Table; /** + * Setting storing a {@link BigDecimal} value. If the precision of + * {@code BigDecimal} is not required {@link LongSetting} or + * {@link DoubleSetting} should be used. * * @author Jens Pelzetter */ @Entity -@Table(name = "CONF_ENTRIES_BIG_DECIMAL", schema = DB_SCHEMA) -public class BigDecimalConfigurationEntry - extends AbstractConfigurationEntry implements Serializable { +@Table(name = "SETTINGS_BIG_DECIMAL", schema = DB_SCHEMA) +public class BigDecimalSetting + extends AbstractSetting implements Serializable { private static final long serialVersionUID = 1869044294174385532L; - + @Column(name = "entry_value") private BigDecimal value; - + @Override public BigDecimal getValue() { return value; } - + @Override public void setValue(final BigDecimal value) { this.value = value; @@ -64,28 +67,28 @@ public class BigDecimalConfigurationEntry if (!super.equals(obj)) { return false; } - + if (obj == null) { return false; } - - if (!(obj instanceof BigDecimalConfigurationEntry)) { + + if (!(obj instanceof BigDecimalSetting)) { return false; } - final BigDecimalConfigurationEntry other - = (BigDecimalConfigurationEntry) obj; + final BigDecimalSetting other + = (BigDecimalSetting) obj; if (!other.canEqual(this)) { return false; } - + return Objects.equals(value, other.getValue()); } @Override public boolean canEqual(final Object obj) { - return obj instanceof BigDecimalConfigurationEntry; + return obj instanceof BigDecimalSetting; } - + @Override public String toString(final String data) { return super.toString(String.format(", value = %s%s", @@ -93,5 +96,4 @@ public class BigDecimalConfigurationEntry data)); } - } diff --git a/ccm-core/src/main/java/org/libreccm/configuration/BooleanConfigurationEntry.java b/ccm-core/src/main/java/org/libreccm/configuration/BooleanSetting.java similarity index 86% rename from ccm-core/src/main/java/org/libreccm/configuration/BooleanConfigurationEntry.java rename to ccm-core/src/main/java/org/libreccm/configuration/BooleanSetting.java index a1e3164e8..603833f30 100644 --- a/ccm-core/src/main/java/org/libreccm/configuration/BooleanConfigurationEntry.java +++ b/ccm-core/src/main/java/org/libreccm/configuration/BooleanSetting.java @@ -27,13 +27,14 @@ import javax.persistence.Entity; import javax.persistence.Table; /** - * + * Setting for storing a boolean value. + * * @author Jens Pelzetter */ @Entity -@Table(name = "CONF_ENTRIES_BOOLEAN", schema = DB_SCHEMA) -public class BooleanConfigurationEntry - extends AbstractConfigurationEntry implements Serializable { +@Table(name = "SETTINGS_BOOLEAN", schema = DB_SCHEMA) +public class BooleanSetting + extends AbstractSetting implements Serializable { private static final long serialVersionUID = -1724350134756734938L; @@ -74,10 +75,10 @@ public class BooleanConfigurationEntry if (obj == null) { return false; } - if (!(obj instanceof BooleanConfigurationEntry)) { + if (!(obj instanceof BooleanSetting)) { return false; } - final BooleanConfigurationEntry other = (BooleanConfigurationEntry) obj; + final BooleanSetting other = (BooleanSetting) obj; if (!other.canEqual(this)) { return false; } @@ -87,7 +88,7 @@ public class BooleanConfigurationEntry @Override public boolean canEqual(final Object obj) { - return obj instanceof BooleanConfigurationEntry; + return obj instanceof BooleanSetting; } @Override diff --git a/ccm-core/src/main/java/org/libreccm/configuration/Configuration.java b/ccm-core/src/main/java/org/libreccm/configuration/Configuration.java index 80baf53d2..30f24aca5 100644 --- a/ccm-core/src/main/java/org/libreccm/configuration/Configuration.java +++ b/ccm-core/src/main/java/org/libreccm/configuration/Configuration.java @@ -40,7 +40,7 @@ public @interface Configuration { * * @return Name of the configuration. */ - String name() default ""; + //String name() default ""; /** * Points to the {@link ResourceBundle} containing the descriptions diff --git a/ccm-core/src/main/java/org/libreccm/configuration/ConfigurationConstants.java b/ccm-core/src/main/java/org/libreccm/configuration/ConfigurationConstants.java index a9082134a..e1627f0e8 100644 --- a/ccm-core/src/main/java/org/libreccm/configuration/ConfigurationConstants.java +++ b/ccm-core/src/main/java/org/libreccm/configuration/ConfigurationConstants.java @@ -19,7 +19,8 @@ package org.libreccm.configuration; /** - * + * Some constants for the configuration system. + * * @author Jens Pelzetter */ public class ConfigurationConstants { diff --git a/ccm-core/src/main/java/org/libreccm/configuration/ConfigurationInfo.java b/ccm-core/src/main/java/org/libreccm/configuration/ConfigurationInfo.java new file mode 100644 index 000000000..fd3840a12 --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/configuration/ConfigurationInfo.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2015 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 java.util.Collections; +import java.util.Locale; +import java.util.NavigableMap; +import java.util.Objects; +import java.util.ResourceBundle; +import java.util.TreeMap; + +/** + * Describes a configuration. Useful for generating user + * interfaces. + * + * @author Jens Pelzetter + */ +public final class ConfigurationInfo { + + /** + * The fully qualified name of the configuration. + */ + private String name; + + /** + * The resource bundle containing the description of the configuration and + * its settings. + */ + private String descBundle; + + /** + * The key for the description of the configuration in the resource bundle. + */ + private String descKey; + + /** + * A navigable map containing a {@link SettingInfo} object for each setting + * of the configuration. + */ + private NavigableMap settings; + + public ConfigurationInfo() { + this.settings = new TreeMap<>(); + } + + public String getName() { + return name; + } + + protected void setName(final String name) { + this.name = name; + } + + public String getDescBundle() { + return descBundle; + } + + protected void setDescBundle(final String descBundle) { + this.descBundle = descBundle; + } + + public ResourceBundle getDescriptionBundle(final Locale locale) { + return ResourceBundle.getBundle(descBundle); + } + + public String getDescKey() { + return descKey; + } + + protected void setDescKey(final String descKey) { + this.descKey = descKey; + } + + public String getDescription(final Locale locale) { + return getDescriptionBundle(locale).getString(descKey); + } + + public NavigableMap getSettings() { + return Collections.unmodifiableNavigableMap(settings); + } + + protected void setSettings(final NavigableMap settings) { + this.settings = settings; + } + + protected void addSetting(final SettingInfo info) { + settings.put(info.getName(), info); + } + + @Override + public int hashCode() { + int hash = 3; + hash = 59 * hash + Objects.hashCode(name); + hash = 59 * hash + Objects.hashCode(descBundle); + hash = 59 * hash + Objects.hashCode(descKey); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (obj instanceof ConfigurationInfo) { + return false; + } + final ConfigurationInfo other = (ConfigurationInfo) obj; + if (!Objects.equals(name, other.getName())) { + return false; + } + if (!Objects.equals(descBundle, other.getDescBundle())) { + return false; + } + return Objects.equals(descKey, other.getDescKey()); + } + + @Override + public String toString() { + return String.format("%s{ " + + "name = \"%s\", " + + "descBundle = \"%s\", " + + "descKey = \"%s\"" + + " }", + name, + descBundle, + descKey); + } + +} 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 6e1e99c59..8f0647be7 100644 --- a/ccm-core/src/main/java/org/libreccm/configuration/ConfigurationManager.java +++ b/ccm-core/src/main/java/org/libreccm/configuration/ConfigurationManager.java @@ -21,41 +21,591 @@ package org.libreccm.configuration; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.persistence.EntityManager; + import org.libreccm.categorization.CategoryManager; import org.libreccm.categorization.Domain; -import org.libreccm.categorization.DomainManager; import org.libreccm.categorization.DomainRepository; 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.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; + /** + * Maps between configuration classes and the values stored in the registry. * * @author Jens Pelzetter */ @RequestScoped public class ConfigurationManager { - + + private static final Logger LOGGER = LogManager.getLogger( + ConfigurationManager.class); + @Inject private CategoryManager categoryManager; - + + @Inject + private CategoryRepository categoryRepository; + @Inject private DomainRepository domainRepository; - - @Inject - private DomainManager domainManager; - + @Inject private EntityManager entityManager; - - - - public AbstractConfigurationEntry getEntry(final String name, - final Class clazz) { - final String[] tokens = name.split("."); - - final Domain registry = domainRepository.findByDomainKey(REGISTRY_DOMAIN); - - throw new UnsupportedOperationException(); + + /** + * Load all settings of the provided 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. + */ + public T findConfiguration(final Class confClass) { + if (confClass == null) { + throw new IllegalArgumentException("confClass 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 = confClass.getName(); + + return findConfiguration(confName, confClass); } - + + /** + * Saves a configuration by writing the values of all setting fields to the + * registry. + * + * @param configuration The configuration to save. The class of the provided + * 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}. + */ + public void saveConfiguration(final Object configuration) { + 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())); + } + + final Field[] fields = configuration.getClass().getDeclaredFields(); + for (final Field field : fields) { + field.setAccessible(true); + try { + setSettingValue(configuration, + 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); + } + } + } + + /** + * 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. + * + * @param configuration The configuration for which the info is generated. + * + * @return a {@link ConfigurationInfo} instance describing the provided + * configuration. + */ + public ConfigurationInfo getConfigurationInfo(final Class configuration) { + 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 annotation = configuration.getAnnotation( + Configuration.class); + + final ConfigurationInfo confInfo = new ConfigurationInfo(); + confInfo.setName(configuration.getClass().getName()); + confInfo.setDescBundle(annotation.descBundle()); + confInfo.setDescKey(annotation.descKey()); + + final Field[] fields = configuration.getDeclaredFields(); + for (final Field field : fields) { + field.setAccessible(true); + if (field.getAnnotation(Setting.class) != null) { + confInfo.addSetting(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. + */ + 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 = 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.setConfigurationClass(configuration.getName()); + + settingInfo.setDescBundle(descBundle); + 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) { + final String[] tokens = name.split("."); + final String[] categoryTokens = Arrays.copyOfRange(tokens, + 0, + tokens.length - 1); + final String categoryPath = String.join(".", categoryTokens); + + final Domain registry = domainRepository + .findByDomainKey(REGISTRY_DOMAIN); + final Category category = categoryRepository.findByPath(registry, + categoryPath); + if (category == null) { + return null; + } + + 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 { + return null; + } + } else { + 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 + * is responsible to do that. Passing a field without the {@code Setting} + * annotation to this method will result in a {@code NullPointerException}. + * + * @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. + */ + private String getSettingName(final Field field) { + final Setting annotation = field.getAnnotation(Setting.class); + + if (annotation.name() == null || annotation.name().isEmpty()) { + return field.getName(); + } else { + return annotation.name(); + } + } + + /** + * 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") + 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 EnumSetting(); + } else if (LocalizedString.class.getName().equals(valueTypeName)) { + return (AbstractSetting) new LocalizedStringSetting(); + } else if (LongSetting.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)); + } + } + + /** + * Sets a value on a setting in the registry. + * + * @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. + */ + 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); + 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); + } + + /** + * 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. + * + * @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. + */ + private T findConfiguration(final String confName, + final Class confClass) { + final Domain registry = domainRepository + .findByDomainKey(REGISTRY_DOMAIN); + final Category category = categoryRepository.findByPath(registry, + confName); + + if (category == null) { + return null; + } + + final T conf; + try { + conf = confClass.newInstance(); + } catch (InstantiationException | IllegalAccessException ex) { + LOGGER.warn(String.format( + "Failed to instantiate configuration \"%s\".", + confClass.getName()), + ex); + return null; + } + + final Field[] fields = confClass.getDeclaredFields(); + for (final Field field : fields) { + field.setAccessible(true); + if (field.getAnnotation(Setting.class) == null) { + continue; + } + + final String settingPath = String.format("%s.%s", + confClass.getName(), + getSettingName(field)); + final Class settingType = field.getType(); + final AbstractSetting setting = findSetting(settingPath, + settingType); + if (setting != null) { + try { + field.set(conf, setting.getValue()); + } catch (IllegalAccessException ex) { + LOGGER.warn(String.format( + "Failed to set value of configuration class \"%s\". " + + "Ignoring.", + confClass.getName()), + ex); + } + } + } + + return conf; + } + } diff --git a/ccm-core/src/main/java/org/libreccm/configuration/DoubleConfigurationEntry.java b/ccm-core/src/main/java/org/libreccm/configuration/DoubleSetting.java similarity index 86% rename from ccm-core/src/main/java/org/libreccm/configuration/DoubleConfigurationEntry.java rename to ccm-core/src/main/java/org/libreccm/configuration/DoubleSetting.java index 98c69cddc..d63b9aca6 100644 --- a/ccm-core/src/main/java/org/libreccm/configuration/DoubleConfigurationEntry.java +++ b/ccm-core/src/main/java/org/libreccm/configuration/DoubleSetting.java @@ -27,13 +27,14 @@ import javax.persistence.Entity; import javax.persistence.Table; /** - * + * A setting for storing a double value. + * * @author Jens Pelzetter */ @Entity -@Table(name = "CONF_ENTRIES_DOUBLE", schema = DB_SCHEMA) -public class DoubleConfigurationEntry - extends AbstractConfigurationEntry implements Serializable { +@Table(name = "SETTINGS_DOUBLE", schema = DB_SCHEMA) +public class DoubleSetting + extends AbstractSetting implements Serializable { private static final long serialVersionUID = -6944518527865528160L; @@ -66,10 +67,10 @@ public class DoubleConfigurationEntry if (obj == null) { return false; } - if (!(obj instanceof DoubleConfigurationEntry)) { + if (!(obj instanceof DoubleSetting)) { return false; } - final DoubleConfigurationEntry other = (DoubleConfigurationEntry) obj; + final DoubleSetting other = (DoubleSetting) obj; if (!other.canEqual(this)) { return false; } @@ -80,7 +81,7 @@ public class DoubleConfigurationEntry @Override public boolean canEqual(final Object obj) { - return obj instanceof DoubleConfigurationEntry; + return obj instanceof DoubleSetting; } @Override diff --git a/ccm-core/src/main/java/org/libreccm/configuration/EnumConfigurationEntry.java b/ccm-core/src/main/java/org/libreccm/configuration/EnumSetting.java similarity index 88% rename from ccm-core/src/main/java/org/libreccm/configuration/EnumConfigurationEntry.java rename to ccm-core/src/main/java/org/libreccm/configuration/EnumSetting.java index 0c088f40e..34fadf5a8 100644 --- a/ccm-core/src/main/java/org/libreccm/configuration/EnumConfigurationEntry.java +++ b/ccm-core/src/main/java/org/libreccm/configuration/EnumSetting.java @@ -32,13 +32,15 @@ import javax.persistence.JoinTable; import javax.persistence.Table; /** - * + * A setting class for storing a list a strings. This can be used to generate + * enums which can be configured by the administrator. + * * @author Jens Pelzetter */ @Entity -@Table(name = "CONF_ENTRIES_ENUM", schema = DB_SCHEMA) -public class EnumConfigurationEntry - extends AbstractConfigurationEntry> implements Serializable { +@Table(name = "SETTINGS_ENUM", schema = DB_SCHEMA) +public class EnumSetting + extends AbstractSetting> implements Serializable { private static final long serialVersionUID = 8506016944203102813L; @@ -86,10 +88,10 @@ public class EnumConfigurationEntry return false; } - if (!(obj instanceof EnumConfigurationEntry)) { + if (!(obj instanceof EnumSetting)) { return false; } - final EnumConfigurationEntry other = (EnumConfigurationEntry) obj; + final EnumSetting other = (EnumSetting) obj; if (!other.canEqual(this)) { return false; } @@ -99,7 +101,7 @@ public class EnumConfigurationEntry @Override public boolean canEqual(final Object obj) { - return obj instanceof EnumConfigurationEntry; + return obj instanceof EnumSetting; } @Override diff --git a/ccm-core/src/main/java/org/libreccm/configuration/LocalizedStringConfigurationEntry.java b/ccm-core/src/main/java/org/libreccm/configuration/LocalizedStringSetting.java similarity index 84% rename from ccm-core/src/main/java/org/libreccm/configuration/LocalizedStringConfigurationEntry.java rename to ccm-core/src/main/java/org/libreccm/configuration/LocalizedStringSetting.java index 17af1efb6..6e3d7525f 100644 --- a/ccm-core/src/main/java/org/libreccm/configuration/LocalizedStringConfigurationEntry.java +++ b/ccm-core/src/main/java/org/libreccm/configuration/LocalizedStringSetting.java @@ -33,13 +33,16 @@ import javax.persistence.JoinTable; import javax.persistence.Table; /** - * + * A setting which stores a {@link LocalizedString} . This can be used for + * storing values for text in the user interface which should be customisable by + * the administrator. + * * @author Jens Pelzetter */ @Entity -@Table(name = "CONF_ENTRIES_L10N_STRING", schema = DB_SCHEMA) -public class LocalizedStringConfigurationEntry - extends AbstractConfigurationEntry implements Serializable { +@Table(name = "SETTINGS_L10N_STRING", schema = DB_SCHEMA) +public class LocalizedStringSetting + extends AbstractSetting implements Serializable { private static final long serialVersionUID = -5854552013878000164L; @@ -79,11 +82,11 @@ public class LocalizedStringConfigurationEntry return false; } - if (!(obj instanceof LocalizedStringConfigurationEntry)) { + if (!(obj instanceof LocalizedStringSetting)) { return false; } - final LocalizedStringConfigurationEntry other - = (LocalizedStringConfigurationEntry) obj; + final LocalizedStringSetting other + = (LocalizedStringSetting) obj; if (!other.canEqual(this)) { return false; } @@ -93,7 +96,7 @@ public class LocalizedStringConfigurationEntry @Override public boolean canEqual(final Object obj) { - return obj instanceof LocalizedStringConfigurationEntry; + return obj instanceof LocalizedStringSetting; } @Override diff --git a/ccm-core/src/main/java/org/libreccm/configuration/LongConfigurationEntry.java b/ccm-core/src/main/java/org/libreccm/configuration/LongSetting.java similarity index 85% rename from ccm-core/src/main/java/org/libreccm/configuration/LongConfigurationEntry.java rename to ccm-core/src/main/java/org/libreccm/configuration/LongSetting.java index d511db6a2..9643f8dd2 100644 --- a/ccm-core/src/main/java/org/libreccm/configuration/LongConfigurationEntry.java +++ b/ccm-core/src/main/java/org/libreccm/configuration/LongSetting.java @@ -27,13 +27,14 @@ import javax.persistence.Entity; import javax.persistence.Table; /** - * + * Setting for storing a long value. + * * @author Jens Pelzetter */ @Entity -@Table(name = "CONF_ENTRIES_INTEGER", schema = DB_SCHEMA) -public class LongConfigurationEntry - extends AbstractConfigurationEntry implements Serializable{ +@Table(name = "SETTINGS_LONG", schema = DB_SCHEMA) +public class LongSetting + extends AbstractSetting implements Serializable{ private static final long serialVersionUID = 818622372461020368L; @@ -66,11 +67,11 @@ public class LongConfigurationEntry if (obj == null) { return false; } - if (!(obj instanceof LongConfigurationEntry)) { + if (!(obj instanceof LongSetting)) { return false; } - final LongConfigurationEntry other - = (LongConfigurationEntry) obj; + final LongSetting other + = (LongSetting) obj; if (!other.canEqual(this)) { return false; } @@ -80,7 +81,7 @@ public class LongConfigurationEntry @Override public boolean canEqual(final Object obj) { - return obj instanceof LongConfigurationEntry; + return obj instanceof LongSetting; } @Override diff --git a/ccm-core/src/main/java/org/libreccm/configuration/SettingInfo.java b/ccm-core/src/main/java/org/libreccm/configuration/SettingInfo.java new file mode 100644 index 000000000..9013084cc --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/configuration/SettingInfo.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2015 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 java.util.Locale; +import java.util.Objects; +import java.util.ResourceBundle; + +/** + * Describes a setting in a configuration class. This class is not designed for + * extension. + * + * @author Jens Pelzetter + */ +public final class SettingInfo { + + /** + * The fully qualified name of the setting. + */ + private String name; + /** + * The type of the setting. + */ + private String valueType; + /** + * The default value of the setting. + */ + private String defaultValue; + /** + * The configuration class to which the setting belongs. + */ + private String configurationClass; + + /** + * ResourceBundle with the description of the setting. + */ + private String descBundle; + + /** + * Key of the description of the setting. + */ + private String descKey; + + public String getName() { + return name; + } + + protected void setName(final String name) { + this.name = name; + } + + public String getValueType() { + return valueType; + } + + protected void setValueType(final String valueType) { + this.valueType = valueType; + } + + public String getDefaultValue() { + return defaultValue; + } + + protected void setDefaultValue(final String defaultValue) { + this.defaultValue = defaultValue; + } + + public String getConfigurationClass() { + return configurationClass; + } + + protected void setConfigurationClass(final String configurationClass) { + this.configurationClass = configurationClass; + } + + public String getDescBundle() { + return descBundle; + } + + protected void setDescBundle(final String descBundle) { + this.descBundle = descBundle; + } + + public ResourceBundle getDescriptionBundle(final Locale locale) { + return ResourceBundle.getBundle(descBundle, locale); + } + + public String getDescKey() { + return descKey; + } + + protected void setDescKey(final String descKey) { + this.descKey = descKey; + } + + public String getDescription(final Locale locale) { + return getDescriptionBundle(locale).getString(descKey); + } + + @Override + public int hashCode() { + int hash = 7; + hash = 79 * hash + Objects.hashCode(name); + hash = 79 * hash + Objects.hashCode(valueType); + hash = 79 * hash + Objects.hashCode(defaultValue); + hash = 79 * hash + Objects.hashCode(configurationClass); + hash = 79 * hash + Objects.hashCode(descBundle); + hash = 79 * hash + Objects.hashCode(descKey); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (obj instanceof SettingInfo) { + return false; + } + final SettingInfo other = (SettingInfo) obj; + if (!Objects.equals(name, other.getName())) { + return false; + } + if (!Objects.equals(valueType, other.getValueType())) { + return false; + } + if (!Objects.equals(defaultValue, other.getDefaultValue())) { + return false; + } + if (!Objects.equals(configurationClass, other.getConfigurationClass())) { + return false; + } + + if (!Objects.equals(descBundle, other.getDescBundle())) { + return false; + } + + return Objects.equals(descKey, other.getDescKey()); + } + + @Override + public String toString() { + return String.format("%s{ " + + "name = \"%s\", " + + "valueType = \"%s\", " + + "defaultValue = \"%s\", " + + "configurationClass = \"%s\", " + + "descBundle = \"%s\"," + + "descKey = \"%s\"" + + " }", + super.toString(), + name, + valueType, + defaultValue, + configurationClass, + descBundle, + descKey); + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/configuration/StringConfigurationEntry.java b/ccm-core/src/main/java/org/libreccm/configuration/StringSetting.java similarity index 86% rename from ccm-core/src/main/java/org/libreccm/configuration/StringConfigurationEntry.java rename to ccm-core/src/main/java/org/libreccm/configuration/StringSetting.java index 3e5ed81c4..885f14821 100644 --- a/ccm-core/src/main/java/org/libreccm/configuration/StringConfigurationEntry.java +++ b/ccm-core/src/main/java/org/libreccm/configuration/StringSetting.java @@ -28,13 +28,14 @@ import javax.persistence.Entity; import javax.persistence.Table; /** - * + * A setting for storing a string value. + * * @author Jens Pelzetter */ @Entity -@Table(name = "CONF_ENTRIES_STRING", schema = DB_SCHEMA) -public class StringConfigurationEntry - extends AbstractConfigurationEntry implements Serializable { +@Table(name = "SETTINGS_STRING", schema = DB_SCHEMA) +public class StringSetting + extends AbstractSetting implements Serializable { private static final long serialVersionUID = -8564570962027541731L; @@ -68,10 +69,10 @@ public class StringConfigurationEntry return false; } - if (!(obj instanceof StringConfigurationEntry)) { + if (!(obj instanceof StringSetting)) { return false; } - final StringConfigurationEntry other = (StringConfigurationEntry) obj; + final StringSetting other = (StringSetting) obj; if (!other.canEqual(this)) { return false; } @@ -81,7 +82,7 @@ public class StringConfigurationEntry @Override public boolean canEqual(final Object obj) { - return obj instanceof StringConfigurationEntry; + return obj instanceof StringSetting; } @Override diff --git a/ccm-core/src/main/java/org/libreccm/configuration/package-info.java b/ccm-core/src/main/java/org/libreccm/configuration/package-info.java new file mode 100644 index 000000000..746197ab7 --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/configuration/package-info.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2015 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 + */ +/** + * This package contains classes which provide a database driven configuration + * system. Prior to version 7.0.0 the configuration was stored in properties + * files and managed using some classes and XML files. + * + * The configuration classes are still used, but they do work differently. + * + * A configuration class is a simple Java bean with several fields which are + * supported by the configuration system, getters and setters for easy + * access and which is annotated with the + * Configuration + * annotation. The settings fields must annotated with the + * Setting + * annotation. + * + * The supported value types are: + *
    + *
  • BigDecimal
  • + *
  • Boolean
  • + *
  • Double
  • + *
  • List of Strings (EnumSetting
  • + *
  • LocalizedString
  • + *
  • Long
  • + *
  • String
  • + *
+ * + * The + * ConfigurationManager + * provides methods for loading and saving the configurations. + * + * For most use cases it should not be necessary to use the classes which + * extends + * AbstractSetting + * outside of this package. But there are may use cases where this is necessary. + * Therefore these classes are publicly visible. + */ +package org.libreccm.configuration; diff --git a/ccm-core/src/main/java/org/libreccm/web/CcmApplication.java b/ccm-core/src/main/java/org/libreccm/web/CcmApplication.java index be3dca149..a1d10f5e6 100644 --- a/ccm-core/src/main/java/org/libreccm/web/CcmApplication.java +++ b/ccm-core/src/main/java/org/libreccm/web/CcmApplication.java @@ -36,11 +36,9 @@ import java.util.Objects; import javax.persistence.Column; import javax.persistence.Entity; -import javax.persistence.JoinColumn; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.OneToMany; -import javax.persistence.OneToOne; import javax.persistence.Table; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; 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 bcac00338..791fb26be 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 @@ -81,46 +81,6 @@ primary key (ROLE_ID) ); - create table CCM_CORE.CONFIGURATION_ENTRIES ( - comment varchar(2048), - OBJECT_ID bigint not null, - primary key (OBJECT_ID) - ); - - create table CCM_CORE.CONF_ENTRIES_BIG_DECIMAL ( - entry_value decimal(19,2), - OBJECT_ID bigint not null, - primary key (OBJECT_ID) - ); - - create table CCM_CORE.CONF_ENTRIES_BOOLEAN ( - entry_value boolean, - OBJECT_ID bigint not null, - primary key (OBJECT_ID) - ); - - create table CCM_CORE.CONF_ENTRIES_DOUBLE ( - entry_value double, - OBJECT_ID bigint not null, - primary key (OBJECT_ID) - ); - - create table CCM_CORE.CONF_ENTRIES_ENUM ( - OBJECT_ID bigint not null, - primary key (OBJECT_ID) - ); - - create table CCM_CORE.CONF_ENTRIES_INTEGER ( - entry_value bigint, - OBJECT_ID bigint not null, - primary key (OBJECT_ID) - ); - - create table CCM_CORE.CONF_ENTRIES_L10N_STRING ( - OBJECT_ID bigint not null, - primary key (OBJECT_ID) - ); - create table CCM_CORE.CONF_ENTRIES_L10N_STR_VALUES ( ENTRY_ID bigint not null, LOCALIZED_VALUE clob, @@ -128,12 +88,6 @@ primary key (ENTRY_ID, LOCALE) ); - create table CCM_CORE.CONF_ENTRIES_STRING ( - entry_value varchar(1024), - OBJECT_ID bigint not null, - primary key (OBJECT_ID) - ); - create table CCM_CORE.DIGESTS ( FREQUENCY integer, HEADER varchar(4096) not null, @@ -518,6 +472,52 @@ primary key (MEMBERSHIP_ID) ); + create table CCM_CORE.SETTINGS ( + name varchar(512) not null, + OBJECT_ID bigint not null, + primary key (OBJECT_ID) + ); + + create table CCM_CORE.SETTINGS_BIG_DECIMAL ( + entry_value decimal(19,2), + OBJECT_ID bigint not null, + primary key (OBJECT_ID) + ); + + create table CCM_CORE.SETTINGS_BOOLEAN ( + entry_value boolean, + OBJECT_ID bigint not null, + primary key (OBJECT_ID) + ); + + create table CCM_CORE.SETTINGS_DOUBLE ( + entry_value double, + OBJECT_ID bigint not null, + primary key (OBJECT_ID) + ); + + create table CCM_CORE.SETTINGS_ENUM ( + OBJECT_ID bigint not null, + primary key (OBJECT_ID) + ); + + create table CCM_CORE.SETTINGS_L10N_STRING ( + OBJECT_ID bigint not null, + primary key (OBJECT_ID) + ); + + create table CCM_CORE.SETTINGS_LONG ( + entry_value bigint, + OBJECT_ID bigint not null, + primary key (OBJECT_ID) + ); + + create table CCM_CORE.SETTINGS_STRING ( + entry_value varchar(1024), + OBJECT_ID bigint not null, + primary key (OBJECT_ID) + ); + create table CCM_CORE.TASK_ASSIGNMENTS ( TASK_ASSIGNMENT_ID bigint not null, ROLE_ID bigint, @@ -678,50 +678,10 @@ foreign key (OBJECT_ID) references CCM_CORE.CATEGORIES; - alter table CCM_CORE.CONFIGURATION_ENTRIES - add constraint FK_8u6h7p0gs4ybf0ju240mggb73 - foreign key (OBJECT_ID) - references CCM_CORE.CCM_OBJECTS; - - alter table CCM_CORE.CONF_ENTRIES_BIG_DECIMAL - add constraint FK_3tnub4je6c2bwi0c3p9m2n6d1 - foreign key (OBJECT_ID) - references CCM_CORE.CONFIGURATION_ENTRIES; - - alter table CCM_CORE.CONF_ENTRIES_BOOLEAN - add constraint FK_d79uxyam5uhhmw3ijw3c14gk2 - foreign key (OBJECT_ID) - references CCM_CORE.CONFIGURATION_ENTRIES; - - alter table CCM_CORE.CONF_ENTRIES_DOUBLE - add constraint FK_l5qxx6wfngl2hvnqaq77oin1s - foreign key (OBJECT_ID) - references CCM_CORE.CONFIGURATION_ENTRIES; - - alter table CCM_CORE.CONF_ENTRIES_ENUM - add constraint FK_blwwj2ht4wbg82meuuxf0t7kk - foreign key (OBJECT_ID) - references CCM_CORE.CONFIGURATION_ENTRIES; - - alter table CCM_CORE.CONF_ENTRIES_INTEGER - add constraint FK_reo0efdw6evf11viwlse1w27 - foreign key (OBJECT_ID) - references CCM_CORE.CONFIGURATION_ENTRIES; - - alter table CCM_CORE.CONF_ENTRIES_L10N_STRING - add constraint FK_dbvyqoliuh0d7bl6ksng4abe - foreign key (OBJECT_ID) - references CCM_CORE.CONFIGURATION_ENTRIES; - alter table CCM_CORE.CONF_ENTRIES_L10N_STR_VALUES add constraint FK_ftb5yqeoli1m932yp3p8ho74g foreign key (ENTRY_ID) - references CCM_CORE.CONF_ENTRIES_L10N_STRING; - - alter table CCM_CORE.CONF_ENTRIES_STRING - add constraint FK_j31m640x2cn0xl5jcbik06708 - foreign key (OBJECT_ID) - references CCM_CORE.CONFIGURATION_ENTRIES; + references CCM_CORE.SETTINGS_L10N_STRING; alter table CCM_CORE.DIGESTS add constraint FK_3xrcpufumqnh4ke4somt89rvh @@ -756,7 +716,7 @@ alter table CCM_CORE.ENUM_CONFIGURATION_ENTRIES_VALUES add constraint FK_ao3evxajxd8y4gy5a6e8ua49j foreign key (ENUM_ID) - references CCM_CORE.CONF_ENTRIES_ENUM; + references CCM_CORE.SETTINGS_ENUM; alter table CCM_CORE.FORMBUILDER_COMPONENTS add constraint FK_72108sd6vsqt88g3fb4kl6o81 @@ -1048,6 +1008,46 @@ foreign key (ROLE_ID) references CCM_CORE.CCM_ROLES; + alter table CCM_CORE.SETTINGS + add constraint FK_3k0t3in140j6wj6eq5olwjgu + foreign key (OBJECT_ID) + references CCM_CORE.CCM_OBJECTS; + + alter table CCM_CORE.SETTINGS_BIG_DECIMAL + add constraint FK_9mbdc1rjkm80edyuijnkwl6ak + foreign key (OBJECT_ID) + references CCM_CORE.SETTINGS; + + alter table CCM_CORE.SETTINGS_BOOLEAN + add constraint FK_1mjjvpjxpwicyv8im6mumc7ug + foreign key (OBJECT_ID) + references CCM_CORE.SETTINGS; + + alter table CCM_CORE.SETTINGS_DOUBLE + add constraint FK_kejnkuyk89tw59xg550kugwb5 + foreign key (OBJECT_ID) + references CCM_CORE.SETTINGS; + + alter table CCM_CORE.SETTINGS_ENUM + add constraint FK_fgrfc2qbl2f2t1l0ku8wo2e5r + foreign key (OBJECT_ID) + references CCM_CORE.SETTINGS; + + alter table CCM_CORE.SETTINGS_L10N_STRING + add constraint FK_evnyfg9udprxmbginhc4o0is9 + foreign key (OBJECT_ID) + references CCM_CORE.SETTINGS; + + alter table CCM_CORE.SETTINGS_LONG + add constraint FK_2l4bw7pbq3koj81cjyoqpenjj + foreign key (OBJECT_ID) + references CCM_CORE.SETTINGS; + + alter table CCM_CORE.SETTINGS_STRING + add constraint FK_naonte6jut7b842icvp9ahino + foreign key (OBJECT_ID) + references CCM_CORE.SETTINGS; + alter table CCM_CORE.TASK_ASSIGNMENTS add constraint FK_klh64or0yq26c63181j1tps2o foreign key (ROLE_ID) 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 8c95bc0f4..59cf2cc4d 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 @@ -81,46 +81,6 @@ primary key (ROLE_ID) ); - create table CCM_CORE.CONFIGURATION_ENTRIES ( - comment varchar(2048), - OBJECT_ID int8 not null, - primary key (OBJECT_ID) - ); - - create table CCM_CORE.CONF_ENTRIES_BIG_DECIMAL ( - entry_value numeric(19, 2), - OBJECT_ID int8 not null, - primary key (OBJECT_ID) - ); - - create table CCM_CORE.CONF_ENTRIES_BOOLEAN ( - entry_value boolean, - OBJECT_ID int8 not null, - primary key (OBJECT_ID) - ); - - create table CCM_CORE.CONF_ENTRIES_DOUBLE ( - entry_value float8, - OBJECT_ID int8 not null, - primary key (OBJECT_ID) - ); - - create table CCM_CORE.CONF_ENTRIES_ENUM ( - OBJECT_ID int8 not null, - primary key (OBJECT_ID) - ); - - create table CCM_CORE.CONF_ENTRIES_INTEGER ( - entry_value int8, - OBJECT_ID int8 not null, - primary key (OBJECT_ID) - ); - - create table CCM_CORE.CONF_ENTRIES_L10N_STRING ( - OBJECT_ID int8 not null, - primary key (OBJECT_ID) - ); - create table CCM_CORE.CONF_ENTRIES_L10N_STR_VALUES ( ENTRY_ID int8 not null, LOCALIZED_VALUE text, @@ -128,12 +88,6 @@ primary key (ENTRY_ID, LOCALE) ); - create table CCM_CORE.CONF_ENTRIES_STRING ( - entry_value varchar(1024), - OBJECT_ID int8 not null, - primary key (OBJECT_ID) - ); - create table CCM_CORE.DIGESTS ( FREQUENCY int4, HEADER varchar(4096) not null, @@ -518,6 +472,52 @@ primary key (MEMBERSHIP_ID) ); + create table CCM_CORE.SETTINGS ( + name varchar(512) not null, + OBJECT_ID int8 not null, + primary key (OBJECT_ID) + ); + + create table CCM_CORE.SETTINGS_BIG_DECIMAL ( + entry_value numeric(19, 2), + OBJECT_ID int8 not null, + primary key (OBJECT_ID) + ); + + create table CCM_CORE.SETTINGS_BOOLEAN ( + entry_value boolean, + OBJECT_ID int8 not null, + primary key (OBJECT_ID) + ); + + create table CCM_CORE.SETTINGS_DOUBLE ( + entry_value float8, + OBJECT_ID int8 not null, + primary key (OBJECT_ID) + ); + + create table CCM_CORE.SETTINGS_ENUM ( + OBJECT_ID int8 not null, + primary key (OBJECT_ID) + ); + + create table CCM_CORE.SETTINGS_L10N_STRING ( + OBJECT_ID int8 not null, + primary key (OBJECT_ID) + ); + + create table CCM_CORE.SETTINGS_LONG ( + entry_value int8, + OBJECT_ID int8 not null, + primary key (OBJECT_ID) + ); + + create table CCM_CORE.SETTINGS_STRING ( + entry_value varchar(1024), + OBJECT_ID int8 not null, + primary key (OBJECT_ID) + ); + create table CCM_CORE.TASK_ASSIGNMENTS ( TASK_ASSIGNMENT_ID int8 not null, ROLE_ID int8, @@ -678,50 +678,10 @@ foreign key (OBJECT_ID) references CCM_CORE.CATEGORIES; - alter table CCM_CORE.CONFIGURATION_ENTRIES - add constraint FK_8u6h7p0gs4ybf0ju240mggb73 - foreign key (OBJECT_ID) - references CCM_CORE.CCM_OBJECTS; - - alter table CCM_CORE.CONF_ENTRIES_BIG_DECIMAL - add constraint FK_3tnub4je6c2bwi0c3p9m2n6d1 - foreign key (OBJECT_ID) - references CCM_CORE.CONFIGURATION_ENTRIES; - - alter table CCM_CORE.CONF_ENTRIES_BOOLEAN - add constraint FK_d79uxyam5uhhmw3ijw3c14gk2 - foreign key (OBJECT_ID) - references CCM_CORE.CONFIGURATION_ENTRIES; - - alter table CCM_CORE.CONF_ENTRIES_DOUBLE - add constraint FK_l5qxx6wfngl2hvnqaq77oin1s - foreign key (OBJECT_ID) - references CCM_CORE.CONFIGURATION_ENTRIES; - - alter table CCM_CORE.CONF_ENTRIES_ENUM - add constraint FK_blwwj2ht4wbg82meuuxf0t7kk - foreign key (OBJECT_ID) - references CCM_CORE.CONFIGURATION_ENTRIES; - - alter table CCM_CORE.CONF_ENTRIES_INTEGER - add constraint FK_reo0efdw6evf11viwlse1w27 - foreign key (OBJECT_ID) - references CCM_CORE.CONFIGURATION_ENTRIES; - - alter table CCM_CORE.CONF_ENTRIES_L10N_STRING - add constraint FK_dbvyqoliuh0d7bl6ksng4abe - foreign key (OBJECT_ID) - references CCM_CORE.CONFIGURATION_ENTRIES; - alter table CCM_CORE.CONF_ENTRIES_L10N_STR_VALUES add constraint FK_ftb5yqeoli1m932yp3p8ho74g foreign key (ENTRY_ID) - references CCM_CORE.CONF_ENTRIES_L10N_STRING; - - alter table CCM_CORE.CONF_ENTRIES_STRING - add constraint FK_j31m640x2cn0xl5jcbik06708 - foreign key (OBJECT_ID) - references CCM_CORE.CONFIGURATION_ENTRIES; + references CCM_CORE.SETTINGS_L10N_STRING; alter table CCM_CORE.DIGESTS add constraint FK_3xrcpufumqnh4ke4somt89rvh @@ -756,7 +716,7 @@ alter table CCM_CORE.ENUM_CONFIGURATION_ENTRIES_VALUES add constraint FK_ao3evxajxd8y4gy5a6e8ua49j foreign key (ENUM_ID) - references CCM_CORE.CONF_ENTRIES_ENUM; + references CCM_CORE.SETTINGS_ENUM; alter table CCM_CORE.FORMBUILDER_COMPONENTS add constraint FK_72108sd6vsqt88g3fb4kl6o81 @@ -1048,6 +1008,46 @@ foreign key (ROLE_ID) references CCM_CORE.CCM_ROLES; + alter table CCM_CORE.SETTINGS + add constraint FK_3k0t3in140j6wj6eq5olwjgu + foreign key (OBJECT_ID) + references CCM_CORE.CCM_OBJECTS; + + alter table CCM_CORE.SETTINGS_BIG_DECIMAL + add constraint FK_9mbdc1rjkm80edyuijnkwl6ak + foreign key (OBJECT_ID) + references CCM_CORE.SETTINGS; + + alter table CCM_CORE.SETTINGS_BOOLEAN + add constraint FK_1mjjvpjxpwicyv8im6mumc7ug + foreign key (OBJECT_ID) + references CCM_CORE.SETTINGS; + + alter table CCM_CORE.SETTINGS_DOUBLE + add constraint FK_kejnkuyk89tw59xg550kugwb5 + foreign key (OBJECT_ID) + references CCM_CORE.SETTINGS; + + alter table CCM_CORE.SETTINGS_ENUM + add constraint FK_fgrfc2qbl2f2t1l0ku8wo2e5r + foreign key (OBJECT_ID) + references CCM_CORE.SETTINGS; + + alter table CCM_CORE.SETTINGS_L10N_STRING + add constraint FK_evnyfg9udprxmbginhc4o0is9 + foreign key (OBJECT_ID) + references CCM_CORE.SETTINGS; + + alter table CCM_CORE.SETTINGS_LONG + add constraint FK_2l4bw7pbq3koj81cjyoqpenjj + foreign key (OBJECT_ID) + references CCM_CORE.SETTINGS; + + alter table CCM_CORE.SETTINGS_STRING + add constraint FK_naonte6jut7b842icvp9ahino + foreign key (OBJECT_ID) + references CCM_CORE.SETTINGS; + alter table CCM_CORE.TASK_ASSIGNMENTS add constraint FK_klh64or0yq26c63181j1tps2o foreign key (ROLE_ID) diff --git a/ccm-core/src/test/java/org/libreccm/configuration/EqualsAndHashCodeTest.java b/ccm-core/src/test/java/org/libreccm/configuration/EqualsAndHashCodeTest.java index 000adf2d3..da13e2643 100644 --- a/ccm-core/src/test/java/org/libreccm/configuration/EqualsAndHashCodeTest.java +++ b/ccm-core/src/test/java/org/libreccm/configuration/EqualsAndHashCodeTest.java @@ -39,13 +39,13 @@ public class EqualsAndHashCodeTest extends EqualsVerifier { @Parameterized.Parameters(name = "{0}") public static Collection> data() { return Arrays.asList(new Class[]{ - BigDecimalConfigurationEntry.class, - BooleanConfigurationEntry.class, - DoubleConfigurationEntry.class, - EnumConfigurationEntry.class, - LocalizedStringConfigurationEntry.class, - LongConfigurationEntry.class, - StringConfigurationEntry.class + BigDecimalSetting.class, + BooleanSetting.class, + DoubleSetting.class, + EnumSetting.class, + LocalizedStringSetting.class, + LongSetting.class, + StringSetting.class }); } diff --git a/ccm-core/src/test/java/org/libreccm/configuration/ToStringTest.java b/ccm-core/src/test/java/org/libreccm/configuration/ToStringTest.java index 84e0b893d..4897fa8c6 100644 --- a/ccm-core/src/test/java/org/libreccm/configuration/ToStringTest.java +++ b/ccm-core/src/test/java/org/libreccm/configuration/ToStringTest.java @@ -38,13 +38,13 @@ public class ToStringTest extends ToStringVerifier { @Parameterized.Parameters(name = "{0}") public static Collection> data() { return Arrays.asList(new Class[]{ - BigDecimalConfigurationEntry.class, - BooleanConfigurationEntry.class, - DoubleConfigurationEntry.class, - EnumConfigurationEntry.class, - LocalizedStringConfigurationEntry.class, - LongConfigurationEntry.class, - StringConfigurationEntry.class + BigDecimalSetting.class, + BooleanSetting.class, + DoubleSetting.class, + EnumSetting.class, + LocalizedStringSetting.class, + LongSetting.class, + StringSetting.class }); } 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 d2503fbbb..5937a150c 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 @@ -5,7 +5,6 @@ DROP SEQUENCE IF EXISTS hibernate_sequence; CREATE SCHEMA ccm_core; - create table CCM_CORE.APPLICATIONS ( APPLICATION_TYPE varchar(1024) not null, PRIMARY_URL varchar(1024) not null, @@ -88,46 +87,6 @@ CREATE SCHEMA ccm_core; primary key (ROLE_ID) ); - create table CCM_CORE.CONFIGURATION_ENTRIES ( - comment varchar(2048), - OBJECT_ID bigint not null, - primary key (OBJECT_ID) - ); - - create table CCM_CORE.CONF_ENTRIES_BIG_DECIMAL ( - entry_value decimal(19,2), - OBJECT_ID bigint not null, - primary key (OBJECT_ID) - ); - - create table CCM_CORE.CONF_ENTRIES_BOOLEAN ( - entry_value boolean, - OBJECT_ID bigint not null, - primary key (OBJECT_ID) - ); - - create table CCM_CORE.CONF_ENTRIES_DOUBLE ( - entry_value double, - OBJECT_ID bigint not null, - primary key (OBJECT_ID) - ); - - create table CCM_CORE.CONF_ENTRIES_ENUM ( - OBJECT_ID bigint not null, - primary key (OBJECT_ID) - ); - - create table CCM_CORE.CONF_ENTRIES_INTEGER ( - entry_value bigint, - OBJECT_ID bigint not null, - primary key (OBJECT_ID) - ); - - create table CCM_CORE.CONF_ENTRIES_L10N_STRING ( - OBJECT_ID bigint not null, - primary key (OBJECT_ID) - ); - create table CCM_CORE.CONF_ENTRIES_L10N_STR_VALUES ( ENTRY_ID bigint not null, LOCALIZED_VALUE clob, @@ -135,12 +94,6 @@ CREATE SCHEMA ccm_core; primary key (ENTRY_ID, LOCALE) ); - create table CCM_CORE.CONF_ENTRIES_STRING ( - entry_value varchar(1024), - OBJECT_ID bigint not null, - primary key (OBJECT_ID) - ); - create table CCM_CORE.DIGESTS ( FREQUENCY integer, HEADER varchar(4096) not null, @@ -525,6 +478,52 @@ CREATE SCHEMA ccm_core; primary key (MEMBERSHIP_ID) ); + create table CCM_CORE.SETTINGS ( + name varchar(512) not null, + OBJECT_ID bigint not null, + primary key (OBJECT_ID) + ); + + create table CCM_CORE.SETTINGS_BIG_DECIMAL ( + entry_value decimal(19,2), + OBJECT_ID bigint not null, + primary key (OBJECT_ID) + ); + + create table CCM_CORE.SETTINGS_BOOLEAN ( + entry_value boolean, + OBJECT_ID bigint not null, + primary key (OBJECT_ID) + ); + + create table CCM_CORE.SETTINGS_DOUBLE ( + entry_value double, + OBJECT_ID bigint not null, + primary key (OBJECT_ID) + ); + + create table CCM_CORE.SETTINGS_ENUM ( + OBJECT_ID bigint not null, + primary key (OBJECT_ID) + ); + + create table CCM_CORE.SETTINGS_L10N_STRING ( + OBJECT_ID bigint not null, + primary key (OBJECT_ID) + ); + + create table CCM_CORE.SETTINGS_LONG ( + entry_value bigint, + OBJECT_ID bigint not null, + primary key (OBJECT_ID) + ); + + create table CCM_CORE.SETTINGS_STRING ( + entry_value varchar(1024), + OBJECT_ID bigint not null, + primary key (OBJECT_ID) + ); + create table CCM_CORE.TASK_ASSIGNMENTS ( TASK_ASSIGNMENT_ID bigint not null, ROLE_ID bigint, @@ -685,50 +684,10 @@ CREATE SCHEMA ccm_core; foreign key (OBJECT_ID) references CCM_CORE.CATEGORIES; - alter table CCM_CORE.CONFIGURATION_ENTRIES - add constraint FK_8u6h7p0gs4ybf0ju240mggb73 - foreign key (OBJECT_ID) - references CCM_CORE.CCM_OBJECTS; - - alter table CCM_CORE.CONF_ENTRIES_BIG_DECIMAL - add constraint FK_3tnub4je6c2bwi0c3p9m2n6d1 - foreign key (OBJECT_ID) - references CCM_CORE.CONFIGURATION_ENTRIES; - - alter table CCM_CORE.CONF_ENTRIES_BOOLEAN - add constraint FK_d79uxyam5uhhmw3ijw3c14gk2 - foreign key (OBJECT_ID) - references CCM_CORE.CONFIGURATION_ENTRIES; - - alter table CCM_CORE.CONF_ENTRIES_DOUBLE - add constraint FK_l5qxx6wfngl2hvnqaq77oin1s - foreign key (OBJECT_ID) - references CCM_CORE.CONFIGURATION_ENTRIES; - - alter table CCM_CORE.CONF_ENTRIES_ENUM - add constraint FK_blwwj2ht4wbg82meuuxf0t7kk - foreign key (OBJECT_ID) - references CCM_CORE.CONFIGURATION_ENTRIES; - - alter table CCM_CORE.CONF_ENTRIES_INTEGER - add constraint FK_reo0efdw6evf11viwlse1w27 - foreign key (OBJECT_ID) - references CCM_CORE.CONFIGURATION_ENTRIES; - - alter table CCM_CORE.CONF_ENTRIES_L10N_STRING - add constraint FK_dbvyqoliuh0d7bl6ksng4abe - foreign key (OBJECT_ID) - references CCM_CORE.CONFIGURATION_ENTRIES; - alter table CCM_CORE.CONF_ENTRIES_L10N_STR_VALUES add constraint FK_ftb5yqeoli1m932yp3p8ho74g foreign key (ENTRY_ID) - references CCM_CORE.CONF_ENTRIES_L10N_STRING; - - alter table CCM_CORE.CONF_ENTRIES_STRING - add constraint FK_j31m640x2cn0xl5jcbik06708 - foreign key (OBJECT_ID) - references CCM_CORE.CONFIGURATION_ENTRIES; + references CCM_CORE.SETTINGS_L10N_STRING; alter table CCM_CORE.DIGESTS add constraint FK_3xrcpufumqnh4ke4somt89rvh @@ -763,7 +722,7 @@ CREATE SCHEMA ccm_core; alter table CCM_CORE.ENUM_CONFIGURATION_ENTRIES_VALUES add constraint FK_ao3evxajxd8y4gy5a6e8ua49j foreign key (ENUM_ID) - references CCM_CORE.CONF_ENTRIES_ENUM; + references CCM_CORE.SETTINGS_ENUM; alter table CCM_CORE.FORMBUILDER_COMPONENTS add constraint FK_72108sd6vsqt88g3fb4kl6o81 @@ -1055,6 +1014,46 @@ CREATE SCHEMA ccm_core; foreign key (ROLE_ID) references CCM_CORE.CCM_ROLES; + alter table CCM_CORE.SETTINGS + add constraint FK_3k0t3in140j6wj6eq5olwjgu + foreign key (OBJECT_ID) + references CCM_CORE.CCM_OBJECTS; + + alter table CCM_CORE.SETTINGS_BIG_DECIMAL + add constraint FK_9mbdc1rjkm80edyuijnkwl6ak + foreign key (OBJECT_ID) + references CCM_CORE.SETTINGS; + + alter table CCM_CORE.SETTINGS_BOOLEAN + add constraint FK_1mjjvpjxpwicyv8im6mumc7ug + foreign key (OBJECT_ID) + references CCM_CORE.SETTINGS; + + alter table CCM_CORE.SETTINGS_DOUBLE + add constraint FK_kejnkuyk89tw59xg550kugwb5 + foreign key (OBJECT_ID) + references CCM_CORE.SETTINGS; + + alter table CCM_CORE.SETTINGS_ENUM + add constraint FK_fgrfc2qbl2f2t1l0ku8wo2e5r + foreign key (OBJECT_ID) + references CCM_CORE.SETTINGS; + + alter table CCM_CORE.SETTINGS_L10N_STRING + add constraint FK_evnyfg9udprxmbginhc4o0is9 + foreign key (OBJECT_ID) + references CCM_CORE.SETTINGS; + + alter table CCM_CORE.SETTINGS_LONG + add constraint FK_2l4bw7pbq3koj81cjyoqpenjj + foreign key (OBJECT_ID) + references CCM_CORE.SETTINGS; + + alter table CCM_CORE.SETTINGS_STRING + add constraint FK_naonte6jut7b842icvp9ahino + foreign key (OBJECT_ID) + references CCM_CORE.SETTINGS; + alter table CCM_CORE.TASK_ASSIGNMENTS add constraint FK_klh64or0yq26c63181j1tps2o foreign key (ROLE_ID) 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 6af1e6b55..8b85d3f18 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 @@ -88,46 +88,6 @@ CREATE SCHEMA ccm_core; primary key (ROLE_ID) ); - create table CCM_CORE.CONFIGURATION_ENTRIES ( - comment varchar(2048), - OBJECT_ID int8 not null, - primary key (OBJECT_ID) - ); - - create table CCM_CORE.CONF_ENTRIES_BIG_DECIMAL ( - entry_value numeric(19, 2), - OBJECT_ID int8 not null, - primary key (OBJECT_ID) - ); - - create table CCM_CORE.CONF_ENTRIES_BOOLEAN ( - entry_value boolean, - OBJECT_ID int8 not null, - primary key (OBJECT_ID) - ); - - create table CCM_CORE.CONF_ENTRIES_DOUBLE ( - entry_value float8, - OBJECT_ID int8 not null, - primary key (OBJECT_ID) - ); - - create table CCM_CORE.CONF_ENTRIES_ENUM ( - OBJECT_ID int8 not null, - primary key (OBJECT_ID) - ); - - create table CCM_CORE.CONF_ENTRIES_INTEGER ( - entry_value int8, - OBJECT_ID int8 not null, - primary key (OBJECT_ID) - ); - - create table CCM_CORE.CONF_ENTRIES_L10N_STRING ( - OBJECT_ID int8 not null, - primary key (OBJECT_ID) - ); - create table CCM_CORE.CONF_ENTRIES_L10N_STR_VALUES ( ENTRY_ID int8 not null, LOCALIZED_VALUE text, @@ -135,12 +95,6 @@ CREATE SCHEMA ccm_core; primary key (ENTRY_ID, LOCALE) ); - create table CCM_CORE.CONF_ENTRIES_STRING ( - entry_value varchar(1024), - OBJECT_ID int8 not null, - primary key (OBJECT_ID) - ); - create table CCM_CORE.DIGESTS ( FREQUENCY int4, HEADER varchar(4096) not null, @@ -525,6 +479,52 @@ CREATE SCHEMA ccm_core; primary key (MEMBERSHIP_ID) ); + create table CCM_CORE.SETTINGS ( + name varchar(512) not null, + OBJECT_ID int8 not null, + primary key (OBJECT_ID) + ); + + create table CCM_CORE.SETTINGS_BIG_DECIMAL ( + entry_value numeric(19, 2), + OBJECT_ID int8 not null, + primary key (OBJECT_ID) + ); + + create table CCM_CORE.SETTINGS_BOOLEAN ( + entry_value boolean, + OBJECT_ID int8 not null, + primary key (OBJECT_ID) + ); + + create table CCM_CORE.SETTINGS_DOUBLE ( + entry_value float8, + OBJECT_ID int8 not null, + primary key (OBJECT_ID) + ); + + create table CCM_CORE.SETTINGS_ENUM ( + OBJECT_ID int8 not null, + primary key (OBJECT_ID) + ); + + create table CCM_CORE.SETTINGS_L10N_STRING ( + OBJECT_ID int8 not null, + primary key (OBJECT_ID) + ); + + create table CCM_CORE.SETTINGS_LONG ( + entry_value int8, + OBJECT_ID int8 not null, + primary key (OBJECT_ID) + ); + + create table CCM_CORE.SETTINGS_STRING ( + entry_value varchar(1024), + OBJECT_ID int8 not null, + primary key (OBJECT_ID) + ); + create table CCM_CORE.TASK_ASSIGNMENTS ( TASK_ASSIGNMENT_ID int8 not null, ROLE_ID int8, @@ -685,50 +685,10 @@ CREATE SCHEMA ccm_core; foreign key (OBJECT_ID) references CCM_CORE.CATEGORIES; - alter table CCM_CORE.CONFIGURATION_ENTRIES - add constraint FK_8u6h7p0gs4ybf0ju240mggb73 - foreign key (OBJECT_ID) - references CCM_CORE.CCM_OBJECTS; - - alter table CCM_CORE.CONF_ENTRIES_BIG_DECIMAL - add constraint FK_3tnub4je6c2bwi0c3p9m2n6d1 - foreign key (OBJECT_ID) - references CCM_CORE.CONFIGURATION_ENTRIES; - - alter table CCM_CORE.CONF_ENTRIES_BOOLEAN - add constraint FK_d79uxyam5uhhmw3ijw3c14gk2 - foreign key (OBJECT_ID) - references CCM_CORE.CONFIGURATION_ENTRIES; - - alter table CCM_CORE.CONF_ENTRIES_DOUBLE - add constraint FK_l5qxx6wfngl2hvnqaq77oin1s - foreign key (OBJECT_ID) - references CCM_CORE.CONFIGURATION_ENTRIES; - - alter table CCM_CORE.CONF_ENTRIES_ENUM - add constraint FK_blwwj2ht4wbg82meuuxf0t7kk - foreign key (OBJECT_ID) - references CCM_CORE.CONFIGURATION_ENTRIES; - - alter table CCM_CORE.CONF_ENTRIES_INTEGER - add constraint FK_reo0efdw6evf11viwlse1w27 - foreign key (OBJECT_ID) - references CCM_CORE.CONFIGURATION_ENTRIES; - - alter table CCM_CORE.CONF_ENTRIES_L10N_STRING - add constraint FK_dbvyqoliuh0d7bl6ksng4abe - foreign key (OBJECT_ID) - references CCM_CORE.CONFIGURATION_ENTRIES; - alter table CCM_CORE.CONF_ENTRIES_L10N_STR_VALUES add constraint FK_ftb5yqeoli1m932yp3p8ho74g foreign key (ENTRY_ID) - references CCM_CORE.CONF_ENTRIES_L10N_STRING; - - alter table CCM_CORE.CONF_ENTRIES_STRING - add constraint FK_j31m640x2cn0xl5jcbik06708 - foreign key (OBJECT_ID) - references CCM_CORE.CONFIGURATION_ENTRIES; + references CCM_CORE.SETTINGS_L10N_STRING; alter table CCM_CORE.DIGESTS add constraint FK_3xrcpufumqnh4ke4somt89rvh @@ -763,7 +723,7 @@ CREATE SCHEMA ccm_core; alter table CCM_CORE.ENUM_CONFIGURATION_ENTRIES_VALUES add constraint FK_ao3evxajxd8y4gy5a6e8ua49j foreign key (ENUM_ID) - references CCM_CORE.CONF_ENTRIES_ENUM; + references CCM_CORE.SETTINGS_ENUM; alter table CCM_CORE.FORMBUILDER_COMPONENTS add constraint FK_72108sd6vsqt88g3fb4kl6o81 @@ -1055,6 +1015,46 @@ CREATE SCHEMA ccm_core; foreign key (ROLE_ID) references CCM_CORE.CCM_ROLES; + alter table CCM_CORE.SETTINGS + add constraint FK_3k0t3in140j6wj6eq5olwjgu + foreign key (OBJECT_ID) + references CCM_CORE.CCM_OBJECTS; + + alter table CCM_CORE.SETTINGS_BIG_DECIMAL + add constraint FK_9mbdc1rjkm80edyuijnkwl6ak + foreign key (OBJECT_ID) + references CCM_CORE.SETTINGS; + + alter table CCM_CORE.SETTINGS_BOOLEAN + add constraint FK_1mjjvpjxpwicyv8im6mumc7ug + foreign key (OBJECT_ID) + references CCM_CORE.SETTINGS; + + alter table CCM_CORE.SETTINGS_DOUBLE + add constraint FK_kejnkuyk89tw59xg550kugwb5 + foreign key (OBJECT_ID) + references CCM_CORE.SETTINGS; + + alter table CCM_CORE.SETTINGS_ENUM + add constraint FK_fgrfc2qbl2f2t1l0ku8wo2e5r + foreign key (OBJECT_ID) + references CCM_CORE.SETTINGS; + + alter table CCM_CORE.SETTINGS_L10N_STRING + add constraint FK_evnyfg9udprxmbginhc4o0is9 + foreign key (OBJECT_ID) + references CCM_CORE.SETTINGS; + + alter table CCM_CORE.SETTINGS_LONG + add constraint FK_2l4bw7pbq3koj81cjyoqpenjj + foreign key (OBJECT_ID) + references CCM_CORE.SETTINGS; + + alter table CCM_CORE.SETTINGS_STRING + add constraint FK_naonte6jut7b842icvp9ahino + foreign key (OBJECT_ID) + references CCM_CORE.SETTINGS; + alter table CCM_CORE.TASK_ASSIGNMENTS add constraint FK_klh64or0yq26c63181j1tps2o foreign key (ROLE_ID) diff --git a/ccm-shortcuts/src/main/java/org/libreccm/shortcuts/ui/ShortcutForm.java b/ccm-shortcuts/src/main/java/org/libreccm/shortcuts/ui/ShortcutForm.java index 3c3478a3e..89cba2b28 100644 --- a/ccm-shortcuts/src/main/java/org/libreccm/shortcuts/ui/ShortcutForm.java +++ b/ccm-shortcuts/src/main/java/org/libreccm/shortcuts/ui/ShortcutForm.java @@ -45,13 +45,12 @@ import com.arsdigita.util.UncheckedWrapperException; import org.apache.log4j.Category; import org.apache.oro.text.perl.Perl5Util; import org.apache.oro.text.perl.MalformedPerl5PatternException; -import org.libreccm.cdi.utils.CdiLookupException; import org.libreccm.cdi.utils.CdiUtil; public class ShortcutForm extends Form { private static final Category log = Category.getInstance(ShortcutForm.class - .getName()); + .getName()); private ParameterSingleSelectionModel m_selected_shortcut; @@ -66,52 +65,56 @@ public class ShortcutForm extends Form { m_selected_shortcut = selected_shortcut; TrimmedStringParameter urlKeyParameter = new TrimmedStringParameter( - "url"); + "url"); urlKeyParameter.addParameterListener(new NotEmptyValidationListener()); m_url = new TextField(urlKeyParameter); TrimmedStringParameter redirectParameter = new TrimmedStringParameter( - "redirect"); + "redirect"); redirectParameter - .addParameterListener(new NotEmptyValidationListener()); + .addParameterListener(new NotEmptyValidationListener()); m_redirect = new TextField(redirectParameter); urlKeyParameter.addParameterListener(new ParameterListener() { + @Override public void validate(ParameterEvent e) throws FormProcessException { ParameterData data = e.getParameterData(); String key = (String) data.getValue(); if (key == null) { - return; // Something else will handle - } // this + return; // Something else will handle this + } Perl5Util perl = new Perl5Util(); try { if (!perl.match("/^(\\/[-a-zA-Z0-9_.]+)+\\/?$/", key)) { data.addError(GlobalizationUtil.globalize( - "shortcuts.ui.invalid_key_descr")); + "shortcuts.ui.invalid_key_descr")); throw new FormProcessException( - "Invalid key", - GlobalizationUtil.globalize("shortcuts.ui.invalid_key") + "Invalid key", + GlobalizationUtil.globalize( + "shortcuts.ui.invalid_key") ); } } catch (MalformedPerl5PatternException ex) { throw new UncheckedWrapperException("bad regex", ex); } } + }); redirectParameter.addParameterListener(new ParameterListener() { + @Override public void validate(ParameterEvent e) throws FormProcessException { ParameterData data = e.getParameterData(); String url = (String) data.getValue(); if (url == null) { - return; // Something else will handle - } // this + return; // Something else will handle this + } url = url.toLowerCase(); @@ -131,11 +134,13 @@ public class ShortcutForm extends Form { } data - .addError("You must enter an absolute path " - + "(starting with '/') or a fully " - + "qualified URL (starting with 'http://' or 'https://')"); - throw new FormProcessException(GlobalizationUtil.globalize("shortcuts.ui.invalid_key")); + .addError("You must enter an absolute path " + + "(starting with '/') or a fully " + + "qualified URL (starting with 'http://' or 'https://')"); + throw new FormProcessException(GlobalizationUtil.globalize( + "shortcuts.ui.invalid_key")); } + }); add(new Label("URL Key:")); @@ -157,14 +162,10 @@ public class ShortcutForm extends Form { public void init(FormSectionEvent ev) throws FormProcessException { final PageState state = ev.getPageState(); final CdiUtil cdiUtil = new CdiUtil(); - final ShortcutRepository shortcutsRepo; - try { - shortcutsRepo = cdiUtil.findBean(ShortcutRepository.class); - } catch (CdiLookupException ex) { - throw new UncheckedWrapperException(ex); - } + final ShortcutRepository shortcutsRepo = cdiUtil.findBean(ShortcutRepository.class); + Long shortcutKey = (Long) m_selected_shortcut - .getSelectedKey(state); + .getSelectedKey(state); if (shortcutKey == null) { log.debug("init form for empty shortcut"); m_url.setValue(state, null); @@ -177,10 +178,11 @@ public class ShortcutForm extends Form { m_redirect.setValue(state, shortcut.getRedirect()); } } + } private class ShortcutFormValidationListener implements - FormValidationListener { + FormValidationListener { @Override public void validate(FormSectionEvent e) throws FormProcessException { @@ -189,7 +191,7 @@ public class ShortcutForm extends Form { // get currently edited shortcut Long shortcutKey = (Long) m_selected_shortcut - .getSelectedKey(state); + .getSelectedKey(state); //TODO: maybe adjust url. see com-arsdigita.shortcuts.ShortcutUtil.cleanURLKey() String key = (String) m_url.getValue(state); @@ -199,10 +201,11 @@ public class ShortcutForm extends Form { if (target != null) { m_url.addError(GlobalizationUtil.globalize( - "shortcuts.ui.key_already_exists")); + "shortcuts.ui.key_already_exists")); throw new FormProcessException( - "duplicate key", - GlobalizationUtil.globalize("shortcuts.ui.duplicate_key") + "duplicate key", + GlobalizationUtil + .globalize("shortcuts.ui.duplicate_key") ); } } @@ -210,6 +213,7 @@ public class ShortcutForm extends Form { int index = key.indexOf("/", 2); String base = key.substring(0, index + 1); } + } private class ShortcutFormProcessListener implements FormProcessListener { @@ -219,12 +223,7 @@ public class ShortcutForm extends Form { ShortcutManager shortcutMgr = new ShortcutManager(); PageState state = e.getPageState(); final CdiUtil cdiUtil = new CdiUtil(); - final ShortcutRepository shortcutsRepo; - try { - shortcutsRepo = cdiUtil.findBean(ShortcutRepository.class); - } catch (CdiLookupException ex) { - throw new UncheckedWrapperException(ex); - } + final ShortcutRepository shortcutsRepo = cdiUtil.findBean(ShortcutRepository.class); Long shortcutKey = (Long) m_selected_shortcut.getSelectedKey(state); @@ -247,11 +246,11 @@ public class ShortcutForm extends Form { } // ShortcutUtil.repopulateShortcuts(); - m_redirect.setValue(state, ""); m_url.setValue(state, ""); m_selected_shortcut.clearSelection(state); } + } } diff --git a/ccm-shortcuts/src/main/java/org/libreccm/shortcuts/ui/ShortcutsTable.java b/ccm-shortcuts/src/main/java/org/libreccm/shortcuts/ui/ShortcutsTable.java index 070e24bef..b085f2e4b 100644 --- a/ccm-shortcuts/src/main/java/org/libreccm/shortcuts/ui/ShortcutsTable.java +++ b/ccm-shortcuts/src/main/java/org/libreccm/shortcuts/ui/ShortcutsTable.java @@ -38,7 +38,6 @@ import com.arsdigita.bebop.ControlLink; import com.arsdigita.util.UncheckedWrapperException; import com.arsdigita.bebop.Component; import java.util.List; -import org.libreccm.cdi.utils.CdiLookupException; import org.libreccm.cdi.utils.CdiUtil; import org.libreccm.shortcuts.ShortcutRepository; @@ -49,7 +48,8 @@ import org.libreccm.shortcuts.ShortcutRepository; public class ShortcutsTable extends Table { private static final Category log - = Category.getInstance(ShortcutsTable.class.getName()); + = Category.getInstance( + ShortcutsTable.class.getName()); public static final String headers[] = {"URL Key", "Redirect", "", ""}; @@ -57,28 +57,27 @@ public class ShortcutsTable extends Table { super(new ShortcutsModelBuilder(), headers); setDefaultCellRenderer(new ShortcutsCellRenderer()); final CdiUtil cdiUtil = new CdiUtil(); - final ShortcutRepository shortcutsRepo; - try { - shortcutsRepo = cdiUtil.findBean(ShortcutRepository.class); - } catch (CdiLookupException ex) { - throw new UncheckedWrapperException(ex); - } + final ShortcutRepository shortcutsRepo = cdiUtil.findBean( + ShortcutRepository.class); addTableActionListener(new TableActionListener() { + public void cellSelected(TableActionEvent e) { selected_shortcut.clearSelection(e.getPageState()); String row = (String) e.getRowKey(); if (e.getColumn().intValue() == 2) { // edit selected log.debug("selected edit shortcut " + row); - selected_shortcut.setSelectedKey(e.getPageState(), new BigDecimal(row)); + selected_shortcut.setSelectedKey(e.getPageState(), + new BigDecimal(row)); } else if (e.getColumn().intValue() == 3) { // delete selected log.fatal("selected delete shortcut " + row); Shortcut shortcut = shortcutsRepo.findById( - (Long) selected_shortcut.getSelectedKey(e.getPageState())); + (Long) selected_shortcut + .getSelectedKey(e.getPageState())); if (shortcut != null) { log.info("delete shortcut " + shortcut.getUrlKey()); @@ -89,11 +88,12 @@ public class ShortcutsTable extends Table { public void headSelected(TableActionEvent e) { } + }); } protected static class ShortcutsModelBuilder extends LockableImpl implements - TableModelBuilder { + TableModelBuilder { public TableModel makeModel(Table table, PageState ps) { return new ShortcutsModel(); @@ -133,14 +133,17 @@ public class ShortcutsTable extends Table { Long id = m_shortcut.getShortcutId(); return id; } + } + } protected static class ShortcutsCellRenderer implements TableCellRenderer { public Component getComponent(Table table, PageState state, - Object value, boolean isSelected, Object key, int row, - int column) { + Object value, boolean isSelected, + Object key, int row, + int column) { Shortcut shortcut = (Shortcut) value; switch (column) { @@ -158,5 +161,7 @@ public class ShortcutsTable extends Table { throw new UncheckedWrapperException("Column out of bounds"); } } + } + }