From f4997f59f774ec652f67e9592cb5a8595dbfbc02 Mon Sep 17 00:00:00 2001 From: Tom Page Date: Thu, 26 Mar 2015 10:43:48 +0000 Subject: [PATCH] RM-2074 Bootstrap initial classification reasons. +review RM git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/HEAD@100134 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../rm-classification-reasons.json | 14 +++ .../classification/ClassificationReason.java | 93 +++++++++++++++++++ .../ClassificationServiceBootstrap.java | 7 +- .../ClassificationServiceImpl.java | 73 +++++++++++++-- .../classification/Configuration.java | 55 +++++++++-- .../classification/ConfigurationUnitTest.java | 50 +++++++--- 6 files changed, 257 insertions(+), 35 deletions(-) create mode 100644 rm-server/config/alfresco/module/org_alfresco_module_rm/classification/rm-classification-reasons.json create mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationReason.java diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/classification/rm-classification-reasons.json b/rm-server/config/alfresco/module/org_alfresco_module_rm/classification/rm-classification-reasons.json new file mode 100644 index 0000000000..9535c91e4c --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/classification/rm-classification-reasons.json @@ -0,0 +1,14 @@ +[ + { + "id" : "1.4(a)", + "displayLabel" : "rm.classification-reason.militaryPlans" + }, + { + "id" : "1.4(b)", + "displayLabel" : "rm.classification-reason.foreignGovernment" + }, + { + "id" : "1.4(c)", + "displayLabel" : "rm.classification.intelligenceActivities" + } +] diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationReason.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationReason.java new file mode 100644 index 0000000000..b95b7cf683 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationReason.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco 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 3 of the License, or + * (at your option) any later version. + * + * Alfresco 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 Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.classification; + +import org.springframework.extensions.surf.util.I18NUtil; + +import java.io.Serializable; + +/** + * This class is a POJO data type for a classification reason. + * + * @author Tom Page + * @since 3.0 + */ +public final class ClassificationReason implements Serializable { + private static final long serialVersionUID = 4876939094239038838L; + private final String id; + private final String displayLabelKey; + + /** + * Constructor to create a classification reason. + * + * @param id + * The unique identifier that represents this classification + * reason. + * @param displayLabelKey + * The I18N key for the display label for the reason. + */ + public ClassificationReason(final String id, final String displayLabelKey) { + if (id == null || id.trim().equals("")) { + throw new IllegalArgumentException("Illegal id: '" + id + "'"); + } + this.id = id; + this.displayLabelKey = displayLabelKey; + } + + /** + * Returns the unique identifier that represents this classification reason. + */ + public String getId() { + return this.id; + } + + /** + * Returns the localised (current locale) display label for this + * classification reason. + */ + public String getDisplayLabel() { + return I18NUtil.getMessage(displayLabelKey); + } + + @Override + public String toString() { + StringBuilder msg = new StringBuilder(); + msg.append(ClassificationReason.class.getSimpleName()).append(":") + .append(id); + + return msg.toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + + ClassificationReason that = (ClassificationReason) o; + + return this.id.equals(that.id); + } + + @Override + public int hashCode() { + return id.hashCode(); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationServiceBootstrap.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationServiceBootstrap.java index 8a5c712471..313c0a0ccb 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationServiceBootstrap.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationServiceBootstrap.java @@ -33,7 +33,7 @@ import org.springframework.extensions.surf.util.AbstractLifecycleBean; public class ClassificationServiceBootstrap extends AbstractLifecycleBean { private final AuthenticationUtil authenticationUtil; - private final ClassificationServiceImpl classificationService; + private final ClassificationServiceImpl classificationServiceImpl; private final TransactionService transactionService; public ClassificationServiceBootstrap(AuthenticationUtil authUtil, @@ -41,7 +41,7 @@ public class ClassificationServiceBootstrap extends AbstractLifecycleBean TransactionService txService) { this.authenticationUtil = authUtil; - this.classificationService = cService; + this.classificationServiceImpl = cService; this.transactionService = txService; } @@ -55,7 +55,8 @@ public class ClassificationServiceBootstrap extends AbstractLifecycleBean { public Void execute() { - classificationService.initConfiguredClassificationLevels(); + classificationServiceImpl.initConfiguredClassificationLevels(); + classificationServiceImpl.initConfiguredClassificationReasons(); return null; } }; diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationServiceImpl.java index bef1fc9b4f..e2d40f913f 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationServiceImpl.java @@ -24,6 +24,8 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.service.cmr.attributes.AttributeService; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.Serializable; import java.util.Collections; @@ -36,21 +38,30 @@ import java.util.List; public class ClassificationServiceImpl extends ServiceBaseImpl implements ClassificationService { - private static Log logger = LogFactory.getLog(ClassificationServiceImpl.class); + private static final Logger LOGGER = LoggerFactory.getLogger(ClassificationServiceImpl.class); private static final String[] LEVELS_KEY = new String[] { "org.alfresco", "module.org_alfresco_module_rm", "classification.levels" }; + private static final String[] REASONS_KEY = new String[] { "org.alfresco", + "module.org_alfresco_module_rm", + "classification.reasons" }; - public static final String DEFAULT_CONFIG_LOCATION = - "/alfresco/module/org_alfresco_module_rm/classification/rm-classification-levels.json"; + + static final String DEFAULT_CONFIG_DIRECTORY = "/alfresco/module/org_alfresco_module_rm/classification/"; + static final String DEFAULT_LEVELS_FILE = DEFAULT_CONFIG_DIRECTORY + + "rm-classification-levels.json"; + static final String DEFAULT_REASONS_FILE = DEFAULT_CONFIG_DIRECTORY + + "rm-classification-reasons.json"; private AttributeService attributeService; // TODO What about other code (e.g. REST API) accessing the AttrService? /** The classification levels currently configured in this server. */ private List configuredLevels; + /** The classification reasons currently configured in this server. */ + private List configuredReasons; - private Configuration config = new Configuration(DEFAULT_CONFIG_LOCATION); + private Configuration config = new Configuration(DEFAULT_LEVELS_FILE, DEFAULT_REASONS_FILE); public void setAttributeService(AttributeService service) { this.attributeService = service; } @@ -59,12 +70,9 @@ public class ClassificationServiceImpl extends ServiceBaseImpl final List allPersistedLevels = getPersistedLevels(); final List configurationLevels = getConfigurationLevels(); - if (logger.isDebugEnabled()) - { - // Note! We cannot log the level names or even the size of these lists for security reasons. - logger.debug("Persisted classification levels: " + loggableStatusOf(allPersistedLevels)); - logger.debug("Classpath classification levels: " + loggableStatusOf(configurationLevels)); - } + // Note! We cannot log the level names or even the size of these lists for security reasons. + LOGGER.debug("Persisted classification levels: {}", loggableStatusOf(allPersistedLevels)); + LOGGER.debug("Classpath classification levels: {}", loggableStatusOf(configurationLevels)); if (configurationLevels == null || configurationLevels.isEmpty()) { @@ -80,6 +88,29 @@ public class ClassificationServiceImpl extends ServiceBaseImpl this.configuredLevels = allPersistedLevels; } } + + void initConfiguredClassificationReasons() { + final List allPersistedReasons = getPersistedReasons(); + final List configurationReasons = getConfigurationReasons(); + + // Note! We cannot log the reasons or even the size of these lists for security reasons. + LOGGER.debug("Persisted classification reasons: {}", loggableStatusOf(allPersistedReasons)); + LOGGER.debug("Classpath classification reasons: {}", loggableStatusOf(configurationReasons)); + + if (configurationReasons == null || configurationReasons.isEmpty()) + { + throw new MissingConfiguration("Classification reason configuration is missing."); + } + else if ( !configurationReasons.equals(allPersistedReasons)) + { + attributeService.setAttribute((Serializable) configurationReasons, REASONS_KEY); + this.configuredReasons = configurationReasons; + } + else + { + this.configuredReasons = allPersistedReasons; + } + } /** Helper method for debug-logging of sensitive lists. */ private String loggableStatusOf(List l) @@ -110,6 +141,28 @@ public class ClassificationServiceImpl extends ServiceBaseImpl { return config.getConfiguredLevels(); } + + /** + * Gets the list of classification reasons as persisted in the system. + * @return the list of classification reasons if they have been persisted, else {@code null}. + */ + List getPersistedReasons() { + return authenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork>() + { + @Override + @SuppressWarnings("unchecked") + public List doWork() throws Exception + { + return (List) attributeService.getAttribute(REASONS_KEY); + } + }); + } + + /** Gets the list of classification reasons - as defined and ordered in the system configuration. */ + List getConfigurationReasons() + { + return config.getConfiguredReasons(); + } @Override public List getClassificationLevels() diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/Configuration.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/Configuration.java index f048805e8c..456cec2816 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/Configuration.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/Configuration.java @@ -18,6 +18,12 @@ */ package org.alfresco.module.org_alfresco_module_rm.classification; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceException.MalformedConfiguration; import org.apache.commons.io.IOUtils; import org.json.JSONArray; @@ -25,12 +31,6 @@ import org.json.JSONException; import org.json.JSONObject; import org.json.JSONTokener; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - /** * This class is responsible for providing the configured classification levels, dealing with * JSON schema as part of that. @@ -40,11 +40,13 @@ import java.util.List; */ class Configuration { - public final String configLocation; + public final String levelConfigLocation; + public final String reasonConfigLocation; - public Configuration(String classpathLocation) + public Configuration(String levelConfigLocation, String reasonConfigLocation) { - this.configLocation = classpathLocation; + this.levelConfigLocation = levelConfigLocation; + this.reasonConfigLocation = reasonConfigLocation; } /** @@ -55,7 +57,7 @@ class Configuration public List getConfiguredLevels() { List result; - try (final InputStream in = this.getClass().getResourceAsStream(configLocation)) + try (final InputStream in = this.getClass().getResourceAsStream(levelConfigLocation)) { if (in == null) { result = Collections.emptyList(); } else @@ -80,4 +82,37 @@ class Configuration } return result; } + + /** + * Gets the list of classification reasons as defined in the system configuration. + * + * @return the configured classification reasons in descending order, or an empty list if there are none. + */ + public List getConfiguredReasons() { + List result; + try (final InputStream in = this.getClass().getResourceAsStream(reasonConfigLocation)) + { + if (in == null) { result = Collections.emptyList(); } + else + { + final String jsonString = IOUtils.toString(in); + final JSONArray jsonArray = new JSONArray(new JSONTokener(jsonString)); + + result = new ArrayList<>(jsonArray.length()); + + for (int i = 0; i < jsonArray.length(); i++) + { + final JSONObject nextObj = jsonArray.getJSONObject(i); + final String id = nextObj.getString("id"); + final String displayLabelKey = nextObj.getString("displayLabel"); + result.add(new ClassificationReason(id, displayLabelKey)); + } + } + } + catch (IOException | JSONException e) + { + throw new MalformedConfiguration("Could not read classification reason configuration", e); + } + return result; + } } diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/classification/ConfigurationUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/classification/ConfigurationUnitTest.java index da358fca68..4fb055b08a 100644 --- a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/classification/ConfigurationUnitTest.java +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/classification/ConfigurationUnitTest.java @@ -18,14 +18,15 @@ */ package org.alfresco.module.org_alfresco_module_rm.classification; -import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceException.MalformedConfiguration; -import org.junit.Test; +import static org.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceImplUnitTest.asLevelList; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import java.util.List; -import static org.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceImplUnitTest.asLevelList; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceException.MalformedConfiguration; +import org.junit.Test; /** * Unit tests for {@link Configuration}. @@ -40,23 +41,48 @@ public class ConfigurationUnitTest "Confidential", "C", "NoClearance", "NC"); - @Test public void readingDefaultConfigurationShouldWork() + @Test public void getConfiguredLevels_readingDefaultConfigurationShouldWork() { - Configuration c = new Configuration(ClassificationServiceImpl.DEFAULT_CONFIG_LOCATION); + Configuration c = new Configuration(ClassificationServiceImpl.DEFAULT_LEVELS_FILE, ClassificationServiceImpl.DEFAULT_REASONS_FILE); List config = c.getConfiguredLevels(); assertEquals(DEFAULT_CLASSIFICATION_LEVELS, config); } - @Test public void readingMissingConfigurationShouldProduceEmptyConfig() throws Exception + @Test public void getConfiguredLevels_readingMissingConfigurationShouldProduceEmptyConfig() throws Exception { - Configuration c = new Configuration("/no/such/resource"); + Configuration c = new Configuration("/no/such/resource", "/no/such/resource"); assertTrue(c.getConfiguredLevels().isEmpty()); } @Test (expected = MalformedConfiguration.class) - public void readingMalformedConfigurationShouldFail() throws Exception + public void getConfiguredLevels_readingMalformedConfigurationShouldFail() { - Configuration c = new Configuration("/alfresco/classification/rm-classification-levels-malformed.json"); - c.getConfiguredLevels(); + Configuration c = new Configuration( + "/alfresco/classification/rm-classification-levels-malformed.json", + "/alfresco/classification/rm-classification-levels-malformed.json"); + c.getConfiguredLevels(); + } + + @Test public void getConfiguredReasons_readingDefaultConfigurationShouldWork() + { + Configuration c = new Configuration(ClassificationServiceImpl.DEFAULT_LEVELS_FILE, + ClassificationServiceImpl.DEFAULT_REASONS_FILE); + List config = c.getConfiguredReasons(); + assertFalse(config.isEmpty()); + } + + @Test public void getConfiguredReasons_readingMissingConfigurationShouldProduceEmptyConfig() throws Exception + { + Configuration c = new Configuration("/no/such/resource", "/no/such/resource"); + assertTrue(c.getConfiguredReasons().isEmpty()); + } + + @Test (expected = MalformedConfiguration.class) + public void getConfiguredReasons_readingMalformedConfigurationShouldFail() + { + Configuration c = new Configuration( + "/alfresco/classification/rm-classification-levels-malformed.json", + "/alfresco/classification/rm-classification-levels-malformed.json"); + c.getConfiguredReasons(); } }