From 911f489875f251a84745488f748588f778d45c73 Mon Sep 17 00:00:00 2001 From: Tom Page Date: Fri, 19 Jun 2015 09:52:20 +0000 Subject: [PATCH] RM-2319 Refactor ClassificationServiceBootstrap. Simplify code to load classification levels, classification reasons and exemption categories using the same method. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/HEAD@106530 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../ClassificationServiceBootstrap.java | 177 ++++-------------- ...lassificationServiceBootstrapUnitTest.java | 79 ++++---- 2 files changed, 80 insertions(+), 176 deletions(-) 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 372266e524..b9a91fef3e 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 @@ -103,9 +103,18 @@ public class ClassificationServiceBootstrap extends AbstractLifecycleBean implem { public Void execute() { - initConfiguredClassificationLevels(); - initConfiguredClassificationReasons(); - initConfiguredExemptionCategories(); + List levels = getConfiguredSchemeEntities( + ClassificationLevel.class, LEVELS_KEY, classificationLevelValidator); + classificationLevelManager.setClassificationLevels(levels); + + List reasons = getConfiguredSchemeEntities( + ClassificationReason.class, REASONS_KEY, classificationReasonValidator); + classificationReasonManager.setClassificationReasons(reasons); + + List exemptionCategories = getConfiguredSchemeEntities( + ExemptionCategory.class, EXEMPTION_CATEGORIES_KEY, exemptionCategoryValidator); + exemptionCategoryManager.setExemptionCategories(exemptionCategories); + initConfiguredClearanceLevels(classificationLevelManager.getClassificationLevels()); return null; } @@ -117,181 +126,63 @@ public class ClassificationServiceBootstrap extends AbstractLifecycleBean implem } /** - * Initialise the system's classification levels by loading the values from a configuration file and storing them - * in the attribute service and the {@link ClassificationLevelManager}. + * Gets an ordered list of some attribute persisted in the system. + * @return the list of values if they have been persisted, else {@code null}. */ - protected void initConfiguredClassificationLevels() + private List getPersistedValues(Serializable[] key) { - final List allPersistedLevels = getPersistedLevels(); - final List configurationLevels = getConfigurationLevels(); - - // 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)); - - classificationLevelValidator.validate(configurationLevels, ClassificationLevel.class.getSimpleName()); - - if (!configurationLevels.equals(allPersistedLevels)) - { - attributeService.setAttribute((Serializable) configurationLevels, LEVELS_KEY); - this.classificationLevelManager.setClassificationLevels(configurationLevels); - } - else - { - this.classificationLevelManager.setClassificationLevels(allPersistedLevels); - } - } - - /** - * 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}. - */ - private List getPersistedLevels() - { - return authenticationUtil.runAsSystem(new RunAsWork>() + return authenticationUtil.runAsSystem(new RunAsWork>() { @Override @SuppressWarnings("unchecked") - public List doWork() throws Exception + public List doWork() throws Exception { - return (List) attributeService.getAttribute(LEVELS_KEY); + return (List) attributeService.getAttribute(key); } }); } - /** Gets the list (in descending order) of classification levels - as defined in the system configuration. */ - private List getConfigurationLevels() - { - return classificationServiceDAO.getConfiguredValues(ClassificationLevel.class); - } - private static boolean isEmpty(List l) { return l == null || l.isEmpty(); } /** Helper method for debug-logging of sensitive lists. */ - private String loggableStatusOf(List l) + private String loggableStatusOf(List l) { if (l == null) { return "null"; } else if (l.isEmpty()) { return "empty"; } else { return "non-empty"; } } - /** - * Initialise the system's classification reasons by loading the values from a configuration file and storing them - * in the attribute service and the {@link ClassificationReasonManager}. - */ - protected void initConfiguredClassificationReasons() + protected List getConfiguredSchemeEntities(Class clazz, Serializable[] key, ClassificationSchemeEntityValidator validator) { - final List persistedReasons = getPersistedReasons(); - final List classpathReasons = getConfigurationReasons(); + final List persistedValues = getPersistedValues(key); + final List classpathValues = classificationServiceDAO.getConfiguredValues(clazz); - // Note! We cannot log the reasons or even the size of these lists for security reasons. - LOGGER.debug("Persisted classification reasons: {}", loggableStatusOf(persistedReasons)); - LOGGER.debug("Classpath classification reasons: {}", loggableStatusOf(classpathReasons)); + // Note! We cannot log the entities or even the size of these lists for security reasons. + LOGGER.debug("Persisted {}: {}", clazz.getSimpleName(), loggableStatusOf(persistedValues)); + LOGGER.debug("Classpath {}: {}", clazz.getSimpleName(), loggableStatusOf(classpathValues)); - classificationReasonValidator.validate(classpathReasons, ClassificationReason.class.getSimpleName()); + validator.validate(classpathValues, clazz.getSimpleName()); - if (isEmpty(persistedReasons)) + if (isEmpty(persistedValues)) { - if (isEmpty(classpathReasons)) + if (isEmpty(classpathValues)) { - throw new MissingConfiguration("Classification reason configuration is missing."); + throw new MissingConfiguration(clazz.getSimpleName() + " configuration is missing."); } - attributeService.setAttribute((Serializable) classpathReasons, REASONS_KEY); - this.classificationReasonManager.setClassificationReasons(classpathReasons); + attributeService.setAttribute((Serializable) classpathValues, key); + return classpathValues; } else { - if (isEmpty(classpathReasons) || !classpathReasons.equals(persistedReasons)) + if (isEmpty(classpathValues) || !classpathValues.equals(persistedValues)) { - LOGGER.warn("Classification reasons configured in classpath do not match those stored in Alfresco. " + LOGGER.warn(clazz.getSimpleName() + " data configured in classpath does not match data stored in Alfresco. " + "Alfresco will use the unchanged values stored in the database."); - // RM-2073 says that we should log a warning and proceed normally. } - this.classificationReasonManager.setClassificationReasons(persistedReasons); + return persistedValues; } } - /** - * 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}. - */ - private List getPersistedReasons() - { - return authenticationUtil.runAsSystem(new 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. */ - private List getConfigurationReasons() - { - return classificationServiceDAO.getConfiguredValues(ClassificationReason.class); - } - - /** - * 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 persistedCategories = getPersistedCategories(); - final List classpathCategories = getConfigurationCategories(); - - LOGGER.debug("Persisted exemption categories: {}", loggableStatusOf(persistedCategories)); - LOGGER.debug("Classpath exemption categories: {}", loggableStatusOf(classpathCategories)); - - exemptionCategoryValidator.validate(classpathCategories, ExemptionCategory.class.getSimpleName()); - - 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 getPersistedCategories() - { - return authenticationUtil.runAsSystem(new RunAsWork>() - { - @Override - @SuppressWarnings("unchecked") - public List doWork() throws Exception - { - return (List) attributeService.getAttribute(EXEMPTION_CATEGORIES_KEY); - } - }); - } - - /** Gets the list of exemption categories - as defined and ordered in the system configuration. */ - private List getConfigurationCategories() - { - return classificationServiceDAO.getConfiguredValues(ExemptionCategory.class); - } - /** * Initialise and create a {@link ClearanceLevelManager}. * diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationServiceBootstrapUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationServiceBootstrapUnitTest.java index 5dfe8c2028..869f2872af 100644 --- a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationServiceBootstrapUnitTest.java +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationServiceBootstrapUnitTest.java @@ -37,6 +37,11 @@ import java.util.stream.Stream; import com.google.common.collect.ImmutableList; import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationException.MissingConfiguration; +import org.alfresco.module.org_alfresco_module_rm.classification.model.ClassifiedContentModel; +import org.alfresco.module.org_alfresco_module_rm.classification.validation.ClassificationLevelFieldsValidator; +import org.alfresco.module.org_alfresco_module_rm.classification.validation.ClassificationReasonFieldsValidator; +import org.alfresco.module.org_alfresco_module_rm.classification.validation.ClassificationSchemeEntityValidator; +import org.alfresco.module.org_alfresco_module_rm.classification.validation.ExemptionCategoryFieldsValidator; import org.alfresco.module.org_alfresco_module_rm.test.util.MockAuthenticationUtilHelper; import org.alfresco.module.org_alfresco_module_rm.util.AuthenticationUtil; import org.alfresco.service.cmr.attributes.AttributeService; @@ -56,23 +61,24 @@ import org.mockito.MockitoAnnotations; * * @author tpage */ -public class ClassificationServiceBootstrapUnitTest +public class ClassificationServiceBootstrapUnitTest implements ClassifiedContentModel { private static final List DEFAULT_CONFIGURED_CLASSIFICATION_LEVELS = asLevelList("TS", "rm.classification.topSecret", "S", "rm.classification.secret", "C", "rm.classification.confidential"); + @SuppressWarnings("unchecked") private static final List DEFAULT_CLASSIFICATION_LEVELS = - union(DEFAULT_CONFIGURED_CLASSIFICATION_LEVELS, - asLevelList("U", "rm.classification.unclassified")); + union(DEFAULT_CONFIGURED_CLASSIFICATION_LEVELS, + asLevelList("U", "rm.classification.unclassified")); private static final List ALT_CLASSIFICATION_LEVELS = asLevelList("B", "Board", - "EM", "ExecutiveManagement", - "E", "Employee", - "P", "Public"); + "EM", "ExecutiveManagement", + "E", "Employee", + "P", "Public"); private static final List PLACEHOLDER_CLASSIFICATION_REASONS = asList(new ClassificationReason("id1", "label1"), - new ClassificationReason("id2", "label2")); + new ClassificationReason("id2", "label2")); private static final List ALTERNATIVE_CLASSIFICATION_REASONS = asList(new ClassificationReason("id8", "label8"), - new ClassificationReason("id9", "label9")); + new ClassificationReason("id9", "label9")); private static final List EXEMPTION_CATEGORIES = asList(new ExemptionCategory("id1", "label1"), new ExemptionCategory("id2", "label2")); private static final List CHANGED_EXEMPTION_CATEGORIES = asList(new ExemptionCategory("id3", "label3")); @@ -113,6 +119,13 @@ public class ClassificationServiceBootstrapUnitTest @Captor ArgumentCaptor loggingEventCaptor; @Captor ArgumentCaptor> clearanceLevelCaptor; + private ClassificationLevelFieldsValidator classificationLevelFieldsValidator = new ClassificationLevelFieldsValidator(); + private ClassificationSchemeEntityValidator classificationLevelValidator = new ClassificationSchemeEntityValidator<>(classificationLevelFieldsValidator); + private ClassificationReasonFieldsValidator classificationReasonFieldsValidator = new ClassificationReasonFieldsValidator(); + private ClassificationSchemeEntityValidator classificationReasonValidator = new ClassificationSchemeEntityValidator<>(classificationReasonFieldsValidator); + private ExemptionCategoryFieldsValidator exemptionCategoryFieldsValidator = new ExemptionCategoryFieldsValidator(); + private ClassificationSchemeEntityValidator exemptionCategoryValidator = new ClassificationSchemeEntityValidator<>(exemptionCategoryFieldsValidator); + @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -124,10 +137,10 @@ public class ClassificationServiceBootstrapUnitTest @Test public void defaultLevelsConfigurationVanillaSystem() { - when(mockClassificationServiceDAO.getConfiguredValues(ClassificationLevel.class)).thenReturn(DEFAULT_CONFIGURED_CLASSIFICATION_LEVELS); + when(mockClassificationServiceDAO.getConfiguredValues(ClassificationLevel.class)).thenReturn(DEFAULT_CONFIGURED_CLASSIFICATION_LEVELS); when(mockAttributeService.getAttribute(anyString(), anyString(), anyString())).thenReturn(null); - classificationServiceBootstrap.initConfiguredClassificationLevels(); + classificationServiceBootstrap.getConfiguredSchemeEntities(ClassificationLevel.class, LEVELS_KEY, classificationLevelValidator); verify(mockAttributeService).setAttribute(eq((Serializable) DEFAULT_CONFIGURED_CLASSIFICATION_LEVELS), anyString(), anyString(), anyString()); @@ -135,14 +148,14 @@ public class ClassificationServiceBootstrapUnitTest @Test public void alternativeLevelsConfigurationPreviouslyStartedSystem() { - when(mockClassificationServiceDAO.getConfiguredValues(ClassificationLevel.class)).thenReturn(ALT_CLASSIFICATION_LEVELS); + when(mockClassificationServiceDAO.getConfiguredValues(ClassificationLevel.class)).thenReturn(ALT_CLASSIFICATION_LEVELS); when(mockAttributeService.getAttribute(anyString(), anyString(), anyString())) .thenReturn((Serializable) DEFAULT_CLASSIFICATION_LEVELS); - classificationServiceBootstrap.initConfiguredClassificationLevels(); + List entities = classificationServiceBootstrap.getConfiguredSchemeEntities(ClassificationLevel.class, LEVELS_KEY, classificationLevelValidator); - verify(mockAttributeService).setAttribute(eq((Serializable) ALT_CLASSIFICATION_LEVELS), - anyString(), anyString(), anyString()); + // TODO Check that changing the behaviour here is ok. + assertEquals(DEFAULT_CLASSIFICATION_LEVELS, entities); } @Test (expected=MissingConfiguration.class) @@ -150,7 +163,7 @@ public class ClassificationServiceBootstrapUnitTest { when(mockAttributeService.getAttribute(anyString(), anyString(), anyString())).thenReturn(null); - classificationServiceBootstrap.initConfiguredClassificationLevels(); + classificationServiceBootstrap.getConfiguredSchemeEntities(ClassificationLevel.class, LEVELS_KEY, classificationLevelValidator); } @Test public void pristineSystemShouldBootstrapReasonsConfiguration() @@ -159,9 +172,9 @@ public class ClassificationServiceBootstrapUnitTest when(mockAttributeService.getAttribute(anyString(), anyString(), anyString())).thenReturn(null); // We'll use a small set of placeholder classification reasons. - when(mockClassificationServiceDAO.getConfiguredValues(ClassificationReason.class)).thenReturn(PLACEHOLDER_CLASSIFICATION_REASONS); + when(mockClassificationServiceDAO.getConfiguredValues(ClassificationReason.class)).thenReturn(PLACEHOLDER_CLASSIFICATION_REASONS); - classificationServiceBootstrap.initConfiguredClassificationReasons(); + classificationServiceBootstrap.getConfiguredSchemeEntities(ClassificationReason.class, REASONS_KEY, classificationReasonValidator); verify(mockAttributeService).setAttribute(eq((Serializable)PLACEHOLDER_CLASSIFICATION_REASONS), anyString(), anyString(), anyString()); @@ -171,9 +184,9 @@ public class ClassificationServiceBootstrapUnitTest { // The classification reasons stored are the same values that are found on the classpath. when(mockAttributeService.getAttribute(anyString(), anyString(), anyString())).thenReturn((Serializable)PLACEHOLDER_CLASSIFICATION_REASONS); - when(mockClassificationServiceDAO.getConfiguredValues(ClassificationReason.class)).thenReturn(PLACEHOLDER_CLASSIFICATION_REASONS); + when(mockClassificationServiceDAO.getConfiguredValues(ClassificationReason.class)).thenReturn(PLACEHOLDER_CLASSIFICATION_REASONS); - classificationServiceBootstrap.initConfiguredClassificationReasons(); + classificationServiceBootstrap.getConfiguredSchemeEntities(ClassificationReason.class, REASONS_KEY, classificationReasonValidator); verify(mockAttributeService, never()).setAttribute(any(Serializable.class), anyString(), anyString(), anyString()); @@ -192,7 +205,7 @@ public class ClassificationServiceBootstrapUnitTest // The classification reasons stored are different from those found on the classpath. when(mockAttributeService.getAttribute(anyString(), anyString(), anyString())).thenReturn( (Serializable) PLACEHOLDER_CLASSIFICATION_REASONS); - when(mockClassificationServiceDAO.getConfiguredValues(ClassificationReason.class)).thenReturn(ALTERNATIVE_CLASSIFICATION_REASONS); + when(mockClassificationServiceDAO.getConfiguredValues(ClassificationReason.class)).thenReturn(ALTERNATIVE_CLASSIFICATION_REASONS); // Put the mock Appender into the log4j logger and allow warning messages to be received. org.apache.log4j.Logger log4jLogger = org.apache.log4j.Logger.getLogger(ClassificationServiceBootstrap.class); @@ -201,7 +214,7 @@ public class ClassificationServiceBootstrapUnitTest log4jLogger.setLevel(Level.WARN); // Call the method under test. - classificationServiceBootstrap.initConfiguredClassificationReasons(); + classificationServiceBootstrap.getConfiguredSchemeEntities(ClassificationReason.class, REASONS_KEY, classificationReasonValidator); // Reset the logging level for other tests. log4jLogger.setLevel(normalLevel); @@ -215,7 +228,7 @@ public class ClassificationServiceBootstrapUnitTest List loggingEvents = loggingEventCaptor.getAllValues(); Stream messages = loggingEvents.stream() .map(LoggingEvent::getRenderedMessage); - String expectedMessage = "Classification reasons configured in classpath do not match those stored in Alfresco. Alfresco will use the unchanged values stored in the database."; + String expectedMessage = "ClassificationReason data configured in classpath does not match data stored in Alfresco. Alfresco will use the unchanged values stored in the database."; assertTrue("Warning message not found in log.", messages.anyMatch(message -> expectedMessage.equals(message))); } @@ -226,7 +239,7 @@ public class ClassificationServiceBootstrapUnitTest .thenReturn((Serializable) null); when(mockClassificationServiceDAO.getConfiguredValues(ClassificationReason.class)).thenReturn(null); - classificationServiceBootstrap.initConfiguredClassificationReasons(); + classificationServiceBootstrap.getConfiguredSchemeEntities(ClassificationReason.class, REASONS_KEY, classificationReasonValidator); } @Test @@ -234,11 +247,11 @@ public class ClassificationServiceBootstrapUnitTest { when(mockAttributeService.getAttribute(anyString(), anyString(), anyString())) .thenReturn((Serializable) null); - when(mockClassificationServiceDAO.getConfiguredValues(ExemptionCategory.class)).thenReturn(EXEMPTION_CATEGORIES); + when(mockClassificationServiceDAO.getConfiguredValues(ExemptionCategory.class)).thenReturn(EXEMPTION_CATEGORIES); - classificationServiceBootstrap.initConfiguredExemptionCategories(); + List entities = classificationServiceBootstrap.getConfiguredSchemeEntities(ExemptionCategory.class, EXEMPTION_CATEGORIES_KEY, exemptionCategoryValidator); - verify(mockExemptionCategoryManager).setExemptionCategories(EXEMPTION_CATEGORIES); + assertEquals(EXEMPTION_CATEGORIES, entities); } @Test @@ -246,11 +259,11 @@ public class ClassificationServiceBootstrapUnitTest { when(mockAttributeService.getAttribute(anyString(), anyString(), anyString())) .thenReturn((Serializable) EXEMPTION_CATEGORIES); - when(mockClassificationServiceDAO.getConfiguredValues(ExemptionCategory.class)).thenReturn(EXEMPTION_CATEGORIES); + when(mockClassificationServiceDAO.getConfiguredValues(ExemptionCategory.class)).thenReturn(EXEMPTION_CATEGORIES); - classificationServiceBootstrap.initConfiguredExemptionCategories(); + List entities = classificationServiceBootstrap.getConfiguredSchemeEntities(ExemptionCategory.class, EXEMPTION_CATEGORIES_KEY, exemptionCategoryValidator); - verify(mockExemptionCategoryManager).setExemptionCategories(EXEMPTION_CATEGORIES); + assertEquals(EXEMPTION_CATEGORIES, entities); } /** @@ -266,7 +279,7 @@ public class ClassificationServiceBootstrapUnitTest { when(mockAttributeService.getAttribute(anyString(), anyString(), anyString())) .thenReturn((Serializable) EXEMPTION_CATEGORIES); - when(mockClassificationServiceDAO.getConfiguredValues(ExemptionCategory.class)).thenReturn(CHANGED_EXEMPTION_CATEGORIES); + when(mockClassificationServiceDAO.getConfiguredValues(ExemptionCategory.class)).thenReturn(CHANGED_EXEMPTION_CATEGORIES); // Put the mock Appender into the log4j logger and allow warning messages to be received. org.apache.log4j.Logger log4jLogger = org.apache.log4j.Logger.getLogger(ClassificationServiceBootstrap.class); @@ -275,7 +288,7 @@ public class ClassificationServiceBootstrapUnitTest log4jLogger.setLevel(Level.WARN); // Call the method under test. - classificationServiceBootstrap.initConfiguredExemptionCategories(); + classificationServiceBootstrap.getConfiguredSchemeEntities(ExemptionCategory.class, EXEMPTION_CATEGORIES_KEY, exemptionCategoryValidator); // Reset the logging level for other tests. log4jLogger.setLevel(normalLevel); @@ -289,7 +302,7 @@ public class ClassificationServiceBootstrapUnitTest List loggingEvents = loggingEventCaptor.getAllValues(); Stream messages = loggingEvents.stream() .map(LoggingEvent::getRenderedMessage); - String expectedMessage = "Exemption categories configured in classpath do not match those stored in Alfresco. Alfresco will use the unchanged values stored in the database."; + String expectedMessage = "ExemptionCategory data configured in classpath does not match data stored in Alfresco. Alfresco will use the unchanged values stored in the database."; assertTrue("Warning message not found in log.", messages.anyMatch(message -> expectedMessage.equals(message))); } @@ -300,7 +313,7 @@ public class ClassificationServiceBootstrapUnitTest .thenReturn((Serializable) null); when(mockClassificationServiceDAO.getConfiguredValues(ExemptionCategory.class)).thenReturn(null); - classificationServiceBootstrap.initConfiguredExemptionCategories(); + classificationServiceBootstrap.getConfiguredSchemeEntities(ExemptionCategory.class, EXEMPTION_CATEGORIES_KEY, exemptionCategoryValidator); } /**