RM-1947 New constraint to avoid invalid classification levels.

Create a service locator class to allow the non-Spring constraint to access
the Spring service.

+review RM-21

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/HEAD@101788 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Tom Page
2015-04-14 09:33:51 +00:00
parent 99576a26a3
commit 7d52c49b2b
10 changed files with 514 additions and 246 deletions

View File

@@ -82,6 +82,10 @@
</property> </property>
</bean> </bean>
<bean id="ClassificationServiceProvider" class="org.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceProvider">
<constructor-arg ref="ClassificationService" index="0" />
</bean>
<bean id="classificationServiceBootstrap" <bean id="classificationServiceBootstrap"
class="org.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceBootstrap"> class="org.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceBootstrap">
<constructor-arg ref="rm.authenticationUtil"/> <constructor-arg ref="rm.authenticationUtil"/>

View File

@@ -26,7 +26,7 @@
<!-- Model Constraints --> <!-- Model Constraints -->
<constraints> <constraints>
<constraint name="clf:classificationLevelRestriction" type="org.alfresco.module.org_alfresco_module_rm.classification.ClassificationLevelConstraint" />
</constraints> </constraints>
<!-- Types --> <!-- Types -->

View File

@@ -0,0 +1,30 @@
/*
* 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.caveat;
/**
* A class to hold I18N keys for messages related to constraint validation.
*
* @author tpage
*/
public class RMConstraintMessageKeys
{
public static final String ERR_NON_STRING = "d_dictionary.constraint.string_length.non_string";
public static final String ERR_INVALID_VALUE = "d_dictionary.constraint.list_of_values.invalid_value";
}

View File

@@ -44,8 +44,6 @@ import org.springframework.extensions.surf.util.I18NUtil;
*/ */
public class RMListOfValuesConstraint extends ListOfValuesConstraint public class RMListOfValuesConstraint extends ListOfValuesConstraint
{ {
private static final String ERR_NON_STRING = "d_dictionary.constraint.string_length.non_string";
private static final String ERR_INVALID_VALUE = "d_dictionary.constraint.list_of_values.invalid_value";
private static final String LOV_CONSTRAINT_VALUE = "listconstraint"; private static final String LOV_CONSTRAINT_VALUE = "listconstraint";
private List<String> allowedValues; private List<String> allowedValues;
private List<String> allowedValuesUpper; private List<String> allowedValuesUpper;
@@ -76,7 +74,7 @@ public class RMListOfValuesConstraint extends ListOfValuesConstraint
sb.append("RMListOfValuesConstraint") sb.append("RMListOfValuesConstraint")
.append("[allowedValues=").append(getAllowedValues()) .append("[allowedValues=").append(getAllowedValues())
.append(", caseSensitive=").append(isCaseSensitive()) .append(", caseSensitive=").append(isCaseSensitive())
.append(", sorted=").append(isCaseSensitive()) .append(", sorted=").append(isSorted())
.append(", matchLogic=").append(getMatchLogic()) .append(", matchLogic=").append(getMatchLogic())
.append("]"); .append("]");
return sb.toString(); return sb.toString();
@@ -231,21 +229,21 @@ public class RMListOfValuesConstraint extends ListOfValuesConstraint
} }
catch (TypeConversionException e) catch (TypeConversionException e)
{ {
throw new ConstraintException(ERR_NON_STRING, value, e); throw new ConstraintException(RMConstraintMessageKeys.ERR_NON_STRING, value, e);
} }
// check that the value is in the set of allowed values // check that the value is in the set of allowed values
if (isCaseSensitive()) if (isCaseSensitive())
{ {
if (!getAllowedValues().contains(valueStr)) if (!getAllowedValues().contains(valueStr))
{ {
throw new ConstraintException(ERR_INVALID_VALUE, value); throw new ConstraintException(RMConstraintMessageKeys.ERR_INVALID_VALUE, value);
} }
} }
else else
{ {
if (!getAllowedValuesUpper().contains(valueStr.toUpperCase())) if (!getAllowedValuesUpper().contains(valueStr.toUpperCase()))
{ {
throw new ConstraintException(ERR_INVALID_VALUE, value); throw new ConstraintException(RMConstraintMessageKeys.ERR_INVALID_VALUE, value);
} }
} }
} }

View File

@@ -50,7 +50,10 @@ public final class ClassificationLevel implements Serializable
/** Returns the unique identifier for this classification level. */ /** Returns the unique identifier for this classification level. */
public String getId() { return this.id; } public String getId() { return this.id; }
/** Returns the localised (current locale) display label for this classification level. */ /**
* Returns the localised (current locale) display label for this classification level. If no translation is found
* then return the key instead.
*/
public String getDisplayLabel() public String getDisplayLabel()
{ {
String message = I18NUtil.getMessage(displayLabelKey); String message = I18NUtil.getMessage(displayLabelKey);

View File

@@ -0,0 +1,96 @@
/*
* 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.ArrayList;
import java.util.List;
import org.alfresco.module.org_alfresco_module_rm.caveat.RMConstraintMessageKeys;
import org.alfresco.repo.dictionary.constraint.AbstractConstraint;
import org.alfresco.service.cmr.dictionary.ConstraintException;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.cmr.repository.datatype.TypeConversionException;
/**
* Check that a value is a valid {@link ClassificationLevel} by checking the {@link ClassificationService}.
*
* @author tpage
*/
public class ClassificationLevelConstraint extends AbstractConstraint
{
/** The classification service provides access to the valid classification levels. */
private ClassificationService classificationService;
/** Constraints must use a default constructor. */
public ClassificationLevelConstraint()
{
super();
this.classificationService = ClassificationServiceProvider.getClassificationService();
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
// TODO Decide whether calling getAllowedValues() is a good idea.
sb.append("ClassificationLevelConstraint")
.append("[allowedValues=").append(getAllowedValues())
.append("]");
return sb.toString();
}
/**
* Get the allowed values. Note that these are <tt>String</tt> instances, but may
* represent non-<tt>String</tt> values. It is up to the caller to distinguish.
*
* @return Returns the values allowed
*/
private List<String> getAllowedValues()
{
List<ClassificationLevel> classificationLevels = classificationService.getClassificationLevels();
List<String> values = new ArrayList<String>();
for (ClassificationLevel classificationLevel : classificationLevels)
{
values.add(classificationLevel.getId());
}
return values;
}
/** {@inheritDoc} */
@Override
protected void evaluateSingleValue(Object value)
{
// convert the value to a String
String valueStr = null;
try
{
valueStr = DefaultTypeConverter.INSTANCE.convert(String.class, value);
}
catch (TypeConversionException e)
{
throw new ConstraintException(RMConstraintMessageKeys.ERR_NON_STRING, value);
}
// Check that the classification level is one of the configured levels.
if (!getAllowedValues().contains(valueStr))
{
throw new ConstraintException(RMConstraintMessageKeys.ERR_INVALID_VALUE, value);
}
}
}

View File

@@ -186,7 +186,8 @@ public class ClassificationServiceImpl extends ServiceBaseImpl
@Override @Override
public List<ClassificationLevel> getClassificationLevels() public List<ClassificationLevel> getClassificationLevels()
{ {
if (configuredLevels == null) { if (configuredLevels == null)
{
return Collections.emptyList(); return Collections.emptyList();
} }
// FIXME Currently assume user has highest security clearance, this should be fixed as part of RM-2112. // FIXME Currently assume user has highest security clearance, this should be fixed as part of RM-2112.

View File

@@ -0,0 +1,59 @@
/*
* 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.concurrent.atomic.AtomicReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A Spring class used to provide the {@link ClassificationService} to non-Spring classes.
*
* @author tpage
*/
public class ClassificationServiceProvider
{
/** Logging utility for the class. */
private static final Logger LOGGER = LoggerFactory.getLogger(ClassificationServiceProvider.class);
/** The Spring application context. */
private static final AtomicReference<ClassificationService> CLASSIFICATION_SERVICE_REF = new AtomicReference<>();
/** Constructor that takes the classification service and makes it available statically. */
public ClassificationServiceProvider(ClassificationService classificationService)
{
ClassificationService oldClassificationService = CLASSIFICATION_SERVICE_REF.getAndSet(classificationService);
if (oldClassificationService != null)
{
LOGGER.debug("Unexpected instantiation of ClassificationServiceProvider has updated reference to classification service.");
}
}
/**
* Get the <code>ClassificationService</code> as defined in the Spring context.
*
* @return The service bean.
*/
public static ClassificationService getClassificationService()
{
return CLASSIFICATION_SERVICE_REF.get();
}
}

View File

@@ -0,0 +1,76 @@
/*
* 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.mockito.Mockito.doReturn;
import java.util.Arrays;
import java.util.List;
import org.alfresco.service.cmr.dictionary.ConstraintException;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
/**
* Unit tests for the {@link ClassificationLevelConstraint}.
*
* @author tpage
*/
public class ClassificationLevelConstraintUnitTest
{
private static final ClassificationLevel LEVEL_ONE = new ClassificationLevel("id1", "DisplayKey1");
private static final ClassificationLevel LEVEL_TWO = new ClassificationLevel("id2", "DisplayKey2");
private static final List<ClassificationLevel> DEFAULT_LEVELS = Arrays.asList(LEVEL_ONE, LEVEL_TWO);
@InjectMocks ClassificationLevelConstraint classificationLevelConstraint;
@Mock ClassificationService mockClassificationService;
@Before
public void setUp()
{
MockitoAnnotations.initMocks(this);
// Currently this list of levels suffices for all the tests.
doReturn(DEFAULT_LEVELS).when(mockClassificationService).getClassificationLevels();
}
/** Check that evaluateSingleValue throws no exceptions when an id is found. */
@Test
public void evaluateSingleValue_valid()
{
classificationLevelConstraint.evaluateSingleValue("id1");
}
/** Check that evaluateSingleValue throws an exception when an id is not found. */
@Test(expected = ConstraintException.class)
public void evaluateSingleValue_stringNotFound()
{
classificationLevelConstraint.evaluateSingleValue("non-existant id");
}
/** Check that evaluateSingleValue throws an exception when supplied with something that isn't a String. */
@Test(expected = ConstraintException.class)
public void evaluateSingleValue_notString()
{
classificationLevelConstraint.evaluateSingleValue(Integer.valueOf(123));
}
}

View File

@@ -28,8 +28,9 @@ import org.junit.runners.Suite;
@RunWith(Suite.class) @RunWith(Suite.class)
@Suite.SuiteClasses( @Suite.SuiteClasses(
{ {
ClassificationServiceImplUnitTest.class, ClassificationLevelConstraintUnitTest.class,
ClassificationServiceDAOUnitTest.class ClassificationServiceDAOUnitTest.class,
ClassificationServiceImplUnitTest.class
}) })
public class ClassificationSuite public class ClassificationSuite
{ {