diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/classification/rm-classification-levels.json b/rm-server/config/alfresco/module/org_alfresco_module_rm/classification/rm-classification-levels.json
new file mode 100644
index 0000000000..65e0b4a8e5
--- /dev/null
+++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/classification/rm-classification-levels.json
@@ -0,0 +1,18 @@
+[
+ {
+ "name" : "TopSecret",
+ "displayLabel" : "rm.classification.topSecret"
+ },
+ {
+ "name" : "Secret",
+ "displayLabel" : "rm.classification.secret"
+ },
+ {
+ "name" : "Confidential",
+ "displayLabel" : "rm.classification.confidential"
+ },
+ {
+ "name" : "NoClearance",
+ "displayLabel" : "rm.classification.noClearance"
+ }
+]
diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/messages/rm-classification.properties b/rm-server/config/alfresco/module/org_alfresco_module_rm/messages/rm-classification.properties
new file mode 100644
index 0000000000..9051979df1
--- /dev/null
+++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/messages/rm-classification.properties
@@ -0,0 +1,5 @@
+## Default roles
+rm.classification.topSecret=Top Secret
+rm.classification.secret=Secret
+rm.classification.confidential=Confidential
+rm.classification.noClearance=No Clearance
diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml
index 0a656298a5..e05cfb4b03 100644
--- a/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml
+++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml
@@ -60,6 +60,7 @@
alfresco.module.org_alfresco_module_rm.messages.dataset-service
alfresco.module.org_alfresco_module_rm.messages.rm-system
alfresco.module.org_alfresco_module_rm.messages.template
+ alfresco.module.org_alfresco_module_rm.messages.rm-classification
@@ -87,7 +88,10 @@
-
+
+
+
+
diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-classified-records-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-classified-records-context.xml
new file mode 100644
index 0000000000..e42ba76443
--- /dev/null
+++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-classified-records-context.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ org.alfresco.module.org_alfresco_module_rm.classification.ClassificationService
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${server.transaction.mode.default}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationLevel.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationLevel.java
new file mode 100644
index 0000000000..dc72ea88fc
--- /dev/null
+++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationLevel.java
@@ -0,0 +1,72 @@
+/*
+ * 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 Level.
+ *
+ * @author Neil Mc Erlean
+ * @since 3.0
+ */
+public final class ClassificationLevel implements Serializable
+{
+ private final String id;
+ private final String displayLabelKey;
+
+ public ClassificationLevel(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 for this classification level. */
+ public String getId() { return this.id; }
+
+ /** Returns the localised (current locale) display label for this classification level. */
+ public String getDisplayLabel() { return I18NUtil.getMessage(displayLabelKey); }
+
+ @Override public String toString()
+ {
+ StringBuilder msg = new StringBuilder();
+ msg.append(ClassificationLevel.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;
+
+ ClassificationLevel that = (ClassificationLevel) 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/ClassificationService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationService.java
new file mode 100644
index 0000000000..52b86c0521
--- /dev/null
+++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationService.java
@@ -0,0 +1,38 @@
+/*
+ * 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 java.util.List;
+
+/**
+ * The Classification Service supports the 'Classified Records' feature, whereby Alfresco
+ * content can be given a {@link ClassificationLevel}. This restricts access to that
+ * content to users having the appropriate security clearance.
+ *
+ * @author Neil Mc Erlean
+ * @since 3.0
+ */
+public interface ClassificationService
+{
+ /**
+ * Returns an immutable list of the defined classification levels.
+ * @return classification levels in descending order from highest to lowest.
+ */
+ public List getApplicableLevels();
+}
diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationServiceException.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationServiceException.java
new file mode 100644
index 0000000000..f024f6e67d
--- /dev/null
+++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationServiceException.java
@@ -0,0 +1,54 @@
+/*
+ * 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.alfresco.error.AlfrescoRuntimeException;
+
+/**
+ * Generic class for any runtime exception thrown within the {@link ClassificationService}.
+ *
+ * @author Neil Mc Erlean
+ * @since 3.0
+ */
+public class ClassificationServiceException extends AlfrescoRuntimeException
+{
+ public ClassificationServiceException(String msgId) { super(msgId); }
+ public ClassificationServiceException(String msgId, Throwable cause) { super(msgId, cause); }
+
+ /** Represents a fatal error due to missing required configuration. */
+ public static class MissingConfiguration extends ClassificationServiceException
+ {
+ public MissingConfiguration(String msgId) { super(msgId); }
+ }
+
+ /** Represents a fatal error due to illegal configuration.
+ * The configuration was understood by the server, but was rejected as illegal. */
+ public static class IllegalConfiguration extends ClassificationServiceException
+ {
+ public IllegalConfiguration(String msgId) { super(msgId); }
+ }
+
+ /** Represents a fatal error due to malformed configuration.
+ * The configuration could not be understood by the server. */
+ public static class MalformedConfiguration extends ClassificationServiceException
+ {
+ public MalformedConfiguration(String msgId) { super(msgId); }
+ public MalformedConfiguration(String msgId, Throwable cause) { super(msgId, cause); }
+ }
+}
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
new file mode 100644
index 0000000000..bb744d2a5e
--- /dev/null
+++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationServiceImpl.java
@@ -0,0 +1,118 @@
+/*
+ * 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.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceException.MissingConfiguration;
+import org.alfresco.module.org_alfresco_module_rm.util.ServiceBaseImpl;
+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 java.io.Serializable;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author Neil Mc Erlean
+ * @since 3.0
+ */
+public class ClassificationServiceImpl extends ServiceBaseImpl
+ implements ClassificationService
+{
+ private static Log logger = LogFactory.getLog(ClassificationServiceImpl.class);
+
+ private static final String[] ATTRIBUTE_KEYS = new String[] { "org.alfresco",
+ "module.org_alfresco_module_rm",
+ "classification.levels" };
+
+ public static final String DEFAULT_CONFIG_LOCATION =
+ "/alfresco/module/org_alfresco_module_rm/classification/rm-classification-levels.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;
+
+ private Configuration config = new Configuration(DEFAULT_CONFIG_LOCATION);
+
+ public void setAttributeService(AttributeService service) { this.attributeService = service; }
+
+ public void initConfiguredClassificationLevels()
+ {
+ final List allPersistedLevels = getPersistedLevels();
+ final List configuredLevels = getConfiguredLevels();
+
+ 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("Configured classification levels: " + loggableStatusOf(configuredLevels));
+ }
+
+ if (configuredLevels == null || configuredLevels.isEmpty())
+ {
+ throw new MissingConfiguration("Classification level configuration is missing.");
+ }
+ else if ( !configuredLevels.equals(allPersistedLevels))
+ {
+ attributeService.setAttribute((Serializable) configuredLevels, ATTRIBUTE_KEYS);
+ this.configuredLevels = configuredLevels;
+ }
+ else
+ {
+ this.configuredLevels = allPersistedLevels;
+ }
+ }
+
+ /** Helper method for debug-logging of sensitive lists. */
+ private String loggableStatusOf(List> l)
+ {
+ if (l == null) { return "null"; }
+ else if (l.isEmpty()) { return "empty"; }
+ else { return "non-empty"; }
+ }
+
+ /**
+ * Gets the list (in descending order) of classification levels - as persisted in the system.
+ * @return the list of classification levels if they have been persisted, else {@code null}.
+ */
+ List getPersistedLevels() {
+ return authenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork>()
+ {
+ @Override public List doWork() throws Exception
+ {
+ return (List) attributeService.getAttribute(ATTRIBUTE_KEYS);
+ }
+ });
+ }
+
+ /** Gets the list (in descending order) of classification levels - as defined in the system configuration. */
+ List getConfiguredLevels()
+ {
+ return config.getConfiguredLevels();
+ }
+
+ @Override
+ public List getApplicableLevels()
+ {
+ return configuredLevels == null ? Collections.emptyList() :
+ Collections.unmodifiableList(configuredLevels);
+ }
+}
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
new file mode 100644
index 0000000000..4906b2898b
--- /dev/null
+++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/Configuration.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2005-2014 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.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceException.MalformedConfiguration;
+import org.apache.commons.io.IOUtils;
+import org.json.JSONArray;
+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.
+ *
+ * @author Neil Mc Erlean
+ * @since 3.0
+ */
+class Configuration
+{
+ public final String configLocation;
+
+ public Configuration(String classpathLocation)
+ {
+ this.configLocation = classpathLocation;
+ }
+
+ /**
+ * Gets the list (in descending order) of classification levels - as defined in the system configuration.
+ *
+ * @return the configured classification levels in descending order, or an empty list if there are none.
+ */
+ public List getConfiguredLevels()
+ {
+ List result;
+ try (final InputStream in = this.getClass().getResourceAsStream(configLocation))
+ {
+ 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 name = nextObj.getString("name");
+ final String displayLabelKey = nextObj.getString("displayLabel");
+ result.add(new ClassificationLevel(name, displayLabelKey));
+ }
+ }
+ }
+ catch (IOException | JSONException e)
+ {
+ throw new MalformedConfiguration("Could not read classification level configuration", e);
+ }
+ return result;
+ }
+}
diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/package-info.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/package-info.java
new file mode 100644
index 0000000000..9bc85dffe2
--- /dev/null
+++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * 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 .
+ */
+
+/**
+ * This package contains the various types required for the 'Classified Records' feature.
+ *
+ * @since 3.0
+ */
+package org.alfresco.module.org_alfresco_module_rm.classification;
\ No newline at end of file
diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationServiceImplUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationServiceImplUnitTest.java
new file mode 100644
index 0000000000..d9b3e85f1b
--- /dev/null
+++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationServiceImplUnitTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2005-2014 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.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceException.MissingConfiguration;
+import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest;
+import org.alfresco.service.cmr.attributes.AttributeService;
+import org.junit.Test;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+
+
+/**
+ * Unit tests for {@link ClassificationServiceImpl}.
+ *
+ * @author Neil Mc Erlean
+ * @since 3.0
+ */
+public class ClassificationServiceImplUnitTest extends BaseUnitTest
+{
+ private static final List DEFAULT_CLASSIFICATION_LEVELS = asLevelList("Top Secret", "rm.classification.topSecret",
+ "Secret", "rm.classification.secret",
+ "Confidential", "rm.classification.confidential",
+ "No Clearance", "rm.classification.noClearance");
+ private static final List ALT_CLASSIFICATION_LEVELS = asLevelList("Board", "B",
+ "Executive Management", "EM",
+ "Employee", "E",
+ "Public", "P");
+ /**
+ * A convenience method for turning lists of level id Strings into lists
+ * of {@code ClassificationLevel} objects.
+ *
+ * @param idsAndLabels A varargs/array of Strings like so: [ id0, label0, id1, label1... ]
+ * @throws IllegalArgumentException if {@code idsAndLabels} has a non-even length.
+ */
+ public static List asLevelList(String ... idsAndLabels)
+ {
+ if (idsAndLabels.length % 2 != 0)
+ {
+ throw new IllegalArgumentException(String.format("Cannot create %s objects with %d args.",
+ ClassificationLevel.class.getSimpleName(), idsAndLabels.length));
+ }
+
+ final int resultLength = idsAndLabels.length / 2;
+ final String[] ids = new String[resultLength];
+ final String[] labels = new String[resultLength];
+
+ for (int i = 0; i < idsAndLabels.length; i++)
+ {
+ if (i % 2 == 0) { ids[i / 2] = idsAndLabels[i]; }
+ else { labels[(i - 1) / 2] = idsAndLabels[i]; }
+ }
+
+ final List levels = new ArrayList<>(resultLength);
+
+ for (int i = 0; i < resultLength; i++)
+ {
+ levels.add(new ClassificationLevel(ids[i], labels[i]));
+ }
+
+ return levels;
+ }
+
+ @Mock(name="attributeService") protected AttributeService mockedAttributeService;
+
+ private ClassificationServiceImpl classificationService;
+
+ @Test public void defaultConfigurationVanillaSystem()
+ {
+ classificationService = new TestClassificationService(null, DEFAULT_CLASSIFICATION_LEVELS);
+ classificationService.setAttributeService(mockedAttributeService);
+
+ classificationService.initConfiguredClassificationLevels();
+
+ assertEquals(DEFAULT_CLASSIFICATION_LEVELS, classificationService.getApplicableLevels());
+ }
+
+ @Test public void alternativeConfigurationVanillaSystem()
+ {
+ classificationService = new TestClassificationService(null, ALT_CLASSIFICATION_LEVELS);
+ classificationService.setAttributeService(mockedAttributeService);
+
+ classificationService.initConfiguredClassificationLevels();
+
+ assertEquals(ALT_CLASSIFICATION_LEVELS, classificationService.getApplicableLevels());
+ }
+
+ @Test public void alternativeConfigurationPreviouslyStartedSystem()
+ {
+ classificationService = new TestClassificationService(DEFAULT_CLASSIFICATION_LEVELS, ALT_CLASSIFICATION_LEVELS);
+ classificationService.setAttributeService(mockedAttributeService);
+
+ classificationService.initConfiguredClassificationLevels();
+
+ assertEquals(ALT_CLASSIFICATION_LEVELS, classificationService.getApplicableLevels());
+ }
+
+ @Test (expected=MissingConfiguration.class)
+ public void missingConfigurationVanillaSystemShouldFail() throws Exception
+ {
+ classificationService = new TestClassificationService(null, null);
+ classificationService.setAttributeService(mockedAttributeService);
+
+ classificationService.initConfiguredClassificationLevels();
+ }
+
+ /**
+ * Helper class for test purposes that allows us to replace the persisted
+ * and configured lists of {@link ClassificationLevel}s.
+ */
+ private static class TestClassificationService extends ClassificationServiceImpl
+ {
+ private final List persisted;
+ private final List configured;
+ public TestClassificationService(List persisted, List configured)
+ {
+ this.persisted = persisted;
+ this.configured = configured;
+ }
+
+ @Override List getPersistedLevels() { return persisted; }
+ @Override List getConfiguredLevels() { return configured; }
+ }
+}
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
new file mode 100644
index 0000000000..f7487e994c
--- /dev/null
+++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/classification/ConfigurationUnitTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2005-2014 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.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceException.MalformedConfiguration;
+import org.junit.Test;
+
+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;
+
+
+/**
+ * Unit tests for {@link Configuration}.
+ *
+ * @author Neil Mc Erlean
+ * @since 3.0
+ */
+public class ConfigurationUnitTest
+{
+ private static final List DEFAULT_CLASSIFICATION_LEVELS = asLevelList("TopSecret", "TS",
+ "Secret", "S",
+ "Confidential", "C",
+ "NoClearance", "NC");
+
+ @Test public void readingDefaultConfigurationShouldWork()
+ {
+ Configuration c = new Configuration(ClassificationServiceImpl.DEFAULT_CONFIG_LOCATION);
+ List config = c.getConfiguredLevels();
+ assertEquals(DEFAULT_CLASSIFICATION_LEVELS, config);
+ }
+
+ @Test public void readingMissingConfigurationShouldProduceEmptyConfig() throws Exception
+ {
+ Configuration c = new Configuration("/no/such/resource");
+ assertTrue(c.getConfiguredLevels().isEmpty());
+ }
+
+ @Test (expected = MalformedConfiguration.class)
+ public void readingMalformedConfigurationShouldFail() throws Exception
+ {
+ Configuration c = new Configuration("/alfresco/classification/rm-classification-levels-malformed.json");
+ c.getConfiguredLevels();
+ }
+}
diff --git a/rm-server/unit-test/resources/alfresco/classification/rm-classification-levels-malformed.json b/rm-server/unit-test/resources/alfresco/classification/rm-classification-levels-malformed.json
new file mode 100644
index 0000000000..b6d2f6b5a1
--- /dev/null
+++ b/rm-server/unit-test/resources/alfresco/classification/rm-classification-levels-malformed.json
@@ -0,0 +1,6 @@
+[
+ {
+ "name" : "TopSecret",
+ "displayLabel" : "Top Secret"
+ }, { {
+]