RM-2319 Bootstrap initial exemption categories into the system.

This change does not include any validation. There is currently no
validation being done for classification reasons either, so it seemed
to make sense to do both in a separate change.

+review RM

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/HEAD@106417 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Tom Page
2015-06-17 15:38:10 +00:00
parent d12292297f
commit ece8e174cb
14 changed files with 479 additions and 14 deletions

View File

@@ -18,12 +18,12 @@
*/
package org.alfresco.module.org_alfresco_module_rm.classification;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.service.cmr.repository.NodeRef;
import java.util.Collections;
import java.util.List;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* Generic class for any runtime exception to do with classified records.
*
@@ -108,6 +108,18 @@ public class ClassificationException extends AlfrescoRuntimeException
}
}
/** The supplied exemption category id was not found in the configured list. */
public static class ExemptionCategoryIdNotFound extends ClassificationException
{
/** serial version uid */
private static final long serialVersionUID = -6754627999115496384L;
public ExemptionCategoryIdNotFound(String id)
{
super("Could not find classification reason with id " + id);
}
}
public static class InvalidNode extends ClassificationException
{
/** serial version uid */

View File

@@ -21,13 +21,13 @@ package org.alfresco.module.org_alfresco_module_rm.classification;
import static java.util.Arrays.asList;
import static org.alfresco.module.org_alfresco_module_rm.util.RMCollectionUtils.getDuplicateElements;
import java.util.ArrayList;
import java.util.List;
import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationException.IllegalAbbreviationChars;
import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationException.IllegalConfiguration;
import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationException.MissingConfiguration;
import java.util.ArrayList;
import java.util.List;
/**
* This class is responsible for validating {@link ClassificationLevel}s.
*
@@ -36,7 +36,7 @@ import java.util.List;
*/
public class ClassificationLevelValidation
{
/** The maximum number of chatacters allowed in a {@link ClassificationLevel#getId() level ID}. */
/** The maximum number of characters allowed in a {@link ClassificationLevel#getId() level ID}. */
private static final int ABBREVIATION_LENGTH_LIMIT = 10;
/**

View File

@@ -46,8 +46,8 @@ public class ClassificationServiceBootstrap extends AbstractLifecycleBean implem
/** Logging utility for the class. */
private static final Logger LOGGER = LoggerFactory.getLogger(ClassificationServiceBootstrap.class);
private final AuthenticationUtil authenticationUtil;
private final TransactionService transactionService;
private final AuthenticationUtil authenticationUtil;
private final TransactionService transactionService;
private AttributeService attributeService;
/** The classification levels currently configured in this server. */
private ClassificationLevelManager classificationLevelManager = new ClassificationLevelManager();
@@ -55,6 +55,8 @@ public class ClassificationServiceBootstrap extends AbstractLifecycleBean implem
private ClassificationReasonManager classificationReasonManager = new ClassificationReasonManager();
/** The clearance levels currently configured in this server. */
private ClearanceLevelManager clearanceLevelManager = new ClearanceLevelManager();
/** The exemption categories currently configured in this server. */
private ExemptionCategoryManager exemptionCategoryManager = new ExemptionCategoryManager();
private ClassificationServiceDAO classificationServiceDAO;
private final ClassificationLevelValidation levelValidation = new ClassificationLevelValidation();
@@ -73,10 +75,13 @@ public class ClassificationServiceBootstrap extends AbstractLifecycleBean implem
public void setClassificationServiceDAO(ClassificationServiceDAO classificationServiceDAO) { this.classificationServiceDAO = classificationServiceDAO; }
public void setAttributeService(AttributeService attributeService) { this.attributeService = attributeService; }
/** Used in unit tests. */
protected void setExemptionCategoryManager(ExemptionCategoryManager exemptionCategoryManager) { this.exemptionCategoryManager = exemptionCategoryManager; }
/** Used in unit tests. */
protected void setClearanceLevelManager(ClearanceLevelManager clearanceLevelManager) { this.clearanceLevelManager = clearanceLevelManager; }
public ClassificationLevelManager getClassificationLevelManager() { return classificationLevelManager; }
public ClassificationReasonManager getClassificationReasonManager() { return classificationReasonManager; }
public ExemptionCategoryManager getExemptionCategoryManager() { return exemptionCategoryManager; }
public ClearanceLevelManager getClearanceLevelManager() { return clearanceLevelManager; }
@Override public void onBootstrap(ApplicationEvent event)
@@ -91,6 +96,7 @@ public class ClassificationServiceBootstrap extends AbstractLifecycleBean implem
{
initConfiguredClassificationLevels();
initConfiguredClassificationReasons();
initConfiguredExemptionCategories();
initConfiguredClearanceLevels(classificationLevelManager.getClassificationLevels());
return null;
}
@@ -173,6 +179,8 @@ public class ClassificationServiceBootstrap extends AbstractLifecycleBean implem
LOGGER.debug("Persisted classification reasons: {}", loggableStatusOf(persistedReasons));
LOGGER.debug("Classpath classification reasons: {}", loggableStatusOf(classpathReasons));
// TODO Add reason validation.
if (isEmpty(persistedReasons))
{
if (isEmpty(classpathReasons))
@@ -217,6 +225,64 @@ public class ClassificationServiceBootstrap extends AbstractLifecycleBean implem
return classificationServiceDAO.getConfiguredReasons();
}
/**
* Initialise the system's exemption categories by loading the values from a configuration file and storing them in
* the attribute service and the {@link ExemptionCategoryManager}.
*/
protected void initConfiguredExemptionCategories()
{
final List<ExemptionCategory> persistedCategories = getPersistedCategories();
final List<ExemptionCategory> classpathCategories = getConfigurationCategories();
LOGGER.debug("Persisted exemption categories: {}", loggableStatusOf(persistedCategories));
LOGGER.debug("Classpath exemption categories: {}", loggableStatusOf(classpathCategories));
// TODO Add exemption category validation.
if (isEmpty(persistedCategories))
{
if (isEmpty(classpathCategories))
{
throw new MissingConfiguration("Exemption category configuration is missing.");
}
attributeService.setAttribute((Serializable) classpathCategories, EXEMPTION_CATEGORIES_KEY);
this.exemptionCategoryManager.setExemptionCategories(classpathCategories);
}
else
{
if (isEmpty(classpathCategories) || !classpathCategories.equals(persistedCategories))
{
LOGGER.warn("Exemption categories configured in classpath do not match those stored in Alfresco. "
+ "Alfresco will use the unchanged values stored in the database.");
// RM-2313 says that we should log a warning and proceed normally.
}
this.exemptionCategoryManager.setExemptionCategories(persistedCategories);
}
}
/**
* Gets the list of exemption categories as persisted in the system.
* @return the list of exemption categories if they have been persisted, else {@code null}.
*/
private List<ExemptionCategory> getPersistedCategories()
{
return authenticationUtil.runAsSystem(new RunAsWork<List<ExemptionCategory>>()
{
@Override
@SuppressWarnings("unchecked")
public List<ExemptionCategory> doWork() throws Exception
{
return (List<ExemptionCategory>) attributeService.getAttribute(EXEMPTION_CATEGORIES_KEY);
}
});
}
/** Gets the list of exemption categories - as defined and ordered in the system configuration. */
private List<ExemptionCategory> getConfigurationCategories()
{
return classificationServiceDAO.getConfiguredExemptionCategories();
}
/**
* Initialise and create a {@link ClearanceLevelManager}.
*

View File

@@ -42,6 +42,7 @@ class ClassificationServiceDAO
{
private String levelConfigLocation;
private String reasonConfigLocation;
private String exemptionCategoryConfigLocation;
/** Set the location of the level configuration file relative to the classpath. */
public void setLevelConfigLocation(String levelConfigLocation) { this.levelConfigLocation = levelConfigLocation; }
@@ -49,6 +50,9 @@ class ClassificationServiceDAO
/** Set the location of the reasons configuration file relative to the classpath. */
public void setReasonConfigLocation(String reasonConfigLocation) { this.reasonConfigLocation = reasonConfigLocation; }
/** Set the location of the exemption categories configuration file relative to the classpath. */
public void setExemptionCategoryConfigLocation(String exemptionCategoryConfigLocation) { this.exemptionCategoryConfigLocation = exemptionCategoryConfigLocation; }
/**
* Gets the list (in descending order) of classification levels - as defined in the classpath.
*
@@ -116,4 +120,38 @@ class ClassificationServiceDAO
}
return result;
}
/**
* Gets the list of exemption categories as defined in the classpath.
*
* @return the configured exemption categories in the given order, or an empty list if there are none.
*/
public List<ExemptionCategory> getConfiguredExemptionCategories()
{
List<ExemptionCategory> result;
try (final InputStream in = this.getClass().getResourceAsStream(exemptionCategoryConfigLocation))
{
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 ExemptionCategory(id, displayLabelKey));
}
}
}
catch (IOException | JSONException e)
{
throw new MalformedConfiguration("Could not read exemption category configuration", e);
}
return result;
}
}

View File

@@ -0,0 +1,87 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.module.org_alfresco_module_rm.classification;
import static org.apache.commons.lang.StringUtils.isNotBlank;
import java.io.Serializable;
import org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck;
import org.springframework.extensions.surf.util.I18NUtil;
/**
* This class is a POJO data type for an exemption category. It gives a reason why a piece of content should not be
* declassified.
*
* @author tpage
* @since 3.0
*/
public final class ExemptionCategory implements Serializable
{
/** serial version uid */
private static final long serialVersionUID = -8990809567320071986L;
private final String id;
private final String displayLabelKey;
public ExemptionCategory(final String id, final String displayLabelKey)
{
RMParameterCheck.checkNotBlank("id", id);
RMParameterCheck.checkNotBlank("displayLabelKey", displayLabelKey);
this.id = id;
this.displayLabelKey = displayLabelKey;
}
/** Returns the unique identifier for this exemption category. */
public String getId() { return this.id; }
/** Returns the key for the display label. */
public String getDisplayLabelKey() { return displayLabelKey; }
/**
* Returns the localised (current locale) display label for this exemption category. If no translation is found then
* return the key instead.
*/
public String getDisplayLabel()
{
String message = I18NUtil.getMessage(displayLabelKey);
return (isNotBlank(message) ? message : displayLabelKey);
}
@Override public String toString()
{
StringBuilder msg = new StringBuilder();
msg.append(ExemptionCategory.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;
ExemptionCategory that = (ExemptionCategory) o;
return this.id.equals(that.id);
}
@Override public int hashCode() { return id.hashCode(); }
}

View File

@@ -0,0 +1,70 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.module.org_alfresco_module_rm.classification;
import java.util.Collection;
import com.google.common.collect.ImmutableList;
import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationException.ExemptionCategoryIdNotFound;
/**
* Container for the configured {@link ExemptionCategory} objects.
*
* @author tpage
*/
public class ExemptionCategoryManager
{
/** An immutable list of exemption categories. */
private ImmutableList<ExemptionCategory> exemptionCategories;
/**
* Store an immutable copy of the given categories.
*
* @param exemptionCategories The exemption categories.
*/
public void setExemptionCategories(Collection<ExemptionCategory> exemptionCategories)
{
this.exemptionCategories = ImmutableList.copyOf(exemptionCategories);
}
/** @return An immutable list of exemption categories. */
public ImmutableList<ExemptionCategory> getExemptionCategories()
{
return exemptionCategories;
}
/**
* Get a <code>ExemptionCategory</code> using its id.
*
* @param id The id of an exemption category.
* @return The exemption category.
* @throws ExemptionCategoryIdNotFound If the exemption category cannot be found.
*/
public ExemptionCategory findCategoryById(String id) throws ExemptionCategoryIdNotFound
{
for (ExemptionCategory exemptionCategory : exemptionCategories)
{
if (exemptionCategory.getId().equals(id))
{
return exemptionCategory;
}
}
throw new ExemptionCategoryIdNotFound(id);
}
}

View File

@@ -38,6 +38,7 @@ public interface ClassifiedContentModel
Serializable[] LEVELS_KEY = new String[] { "org.alfresco", "module.org_alfresco_module_rm", "classification.levels" };
Serializable[] REASONS_KEY = new String[] { "org.alfresco", "module.org_alfresco_module_rm", "classification.reasons" };
Serializable[] EXEMPTION_CATEGORIES_KEY = new String[] { "org.alfresco", "module.org_alfresco_module_rm", "classification.exemptionCategories" };
/** Classified aspect */
QName ASPECT_CLASSIFIED = QName.createQName(CLF_URI, "classified");