RM-2130 Support for specific instantiable collections.

It's impossible to instantiate/clone a generic collection, so if a member of the collection
changes then our only option is to attempt to remove it.  This has two problems. Firstly,
this is far more brutal than we desire in many cases. Secondly, some collections do not
implement the remove method (e.g. anything returned by Arrays.asList()).

As an attempt to work around this issue we've created some specific implementation
processors. This introduces new potential problems - e.g. if someone has used an exotic
type of list it will be replaced with an ArrayList by the ListPostMethodInvocationProcessor.

+review RM

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/DEV/ENFORCE@107355 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Tom Page
2015-06-29 15:18:49 +00:00
parent a476d21d08
commit e9a5f28f85
9 changed files with 614 additions and 109 deletions

View File

@@ -24,7 +24,7 @@ import java.util.Iterator;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
/** /**
* Collection Post Method Invocation Processor * Collection Post Method Invocation Processor.
* *
* @author Tuna Aksoy * @author Tuna Aksoy
* @since 3.0 * @since 3.0
@@ -44,40 +44,81 @@ public class CollectionPostMethodInvocationProcessor extends BasePostMethodInvoc
/** /**
* @see org.alfresco.module.org_alfresco_module_rm.classification.interceptor.processor.BasePostMethodInvocationProcessor#process(java.lang.Object) * @see org.alfresco.module.org_alfresco_module_rm.classification.interceptor.processor.BasePostMethodInvocationProcessor#process(java.lang.Object)
*/ */
@SuppressWarnings("rawtypes") @SuppressWarnings({ "rawtypes", "unchecked" })
@Override @Override
public <T> T process(T object) public <T> T process(T object)
{ {
T result = object; Collection collection = ((Collection) object);
if (result != null) if (collection != null)
{ {
Collection collection = ((Collection) result); BasePostMethodInvocationProcessor processor = pickProcessor(collection);
if (!collection.isEmpty()) if (processor != null)
{ {
Iterator iterator = collection.iterator(); object = (T) processCollection(collection, processor);
while (iterator.hasNext())
{
Object next = iterator.next();
// TODO: Can we guarantee that all the elements of a collection can be processed by the same processor?
BasePostMethodInvocationProcessor processor = getPostMethodInvocationProcessor().getProcessor(next);
if (processor != null)
{
Object processed = processor.process(next);
if (processed == null)
{
iterator.remove();
}
else if (!processed.equals(next))
{
// TODO Support this, as it will be hit by e.g. collections of collections.
throw new IllegalStateException("Modifying members of a collection is not yet supported.");
}
}
}
} }
} }
return result; return object;
}
/**
* Process a collection using the supplied processor.
*
* @param collection The collection to be processed.
* @param processor A collection suitable for access by someone with the current security clearance.
*/
protected <T> Collection<T> processCollection(Collection<T> collection, BasePostMethodInvocationProcessor processor)
{
Iterator<T> iterator = collection.iterator();
while (iterator.hasNext())
{
Object next = iterator.next();
Object processed = processor.process(next);
try
{
if (processed == null)
{
iterator.remove();
}
else if (!processed.equals(next))
{
// Modifying members of this type of collection is not supported, so filter the whole collection.
return null;
}
}
catch (UnsupportedOperationException e)
{
// If the collection cannot be modified and it contains classified data then the whole thing must be filtered.
return null;
}
}
return collection;
}
/**
* Pick a suitable processor for the members of the collection. We assume that all the elements of a collection can
* be processed by the same processor.
*
* @param collection The collection to be processed.
* @return The chosen processor, or {@code null} if no suitable processor could be found.
*/
@SuppressWarnings("rawtypes")
private BasePostMethodInvocationProcessor pickProcessor(Collection collection)
{
Iterator iterator = collection.iterator();
while (iterator.hasNext())
{
Object next = iterator.next();
if (next != null)
{
BasePostMethodInvocationProcessor processor = getPostMethodInvocationProcessor().getProcessor(next);
if (processor != null)
{
return processor;
}
}
}
return null;
} }
} }

View File

@@ -0,0 +1,47 @@
/*
* 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.interceptor.processor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* List Post Method Invocation Processor. This replaces the existing list with a filtered {@link ArrayList}. By doing
* this we gain the ability to replace members of a list, which is not possible using the
* {@link CollectionPostMethodInvocationProcessor}. The downside is that whatever type of list was provided gets
* replaced with an {@code ArrayList}.
*
* @author Tom Page
* @since 3.0
*/
public class ListPostMethodInvocationProcessor extends ModifiableCollectionPostMethodInvocationProcessor
{
@Override
protected Class<?> getClassName()
{
return List.class;
}
@Override
protected <T> Collection<T> createEmptyCollection(Collection<T> collection)
{
return new ArrayList<>();
}
}

View File

@@ -0,0 +1,61 @@
/*
* 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.interceptor.processor;
import java.util.ArrayList;
import java.util.Collection;
/**
* List Post Method Invocation Processor. This replaces the existing list with a filtered {@link ArrayList}. By doing
* this we gain the ability to replace members of a list, which is not possible using the
* {@link CollectionPostMethodInvocationProcessor}. The downside is that whatever type of list was provided gets
* replaced with an {@code ArrayList}.
*
* @author Tom Page
* @since 3.0
*/
public abstract class ModifiableCollectionPostMethodInvocationProcessor extends CollectionPostMethodInvocationProcessor
{
@Override
abstract protected Class<?> getClassName();
/**
* Create an empty modifiable collection.
*
* @param collection The source collection to try to mimic.
* @return The new empty collection.
*/
abstract protected <T> Collection<T> createEmptyCollection(Collection<T> collection);
/** {@inheritDoc} */
@Override
protected <T> Collection<T> processCollection(Collection<T> collection, BasePostMethodInvocationProcessor processor)
{
Collection<T> returnList = createEmptyCollection(collection);
for (T member : collection)
{
T processed = processor.process(member);
if (processed != null)
{
returnList.add(processed);
}
}
return returnList;
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.interceptor.processor;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
/**
* Set Post Method Invocation Processor. This replaces the existing set with a filtered {@link HashSet}. By doing
* this we gain the ability to replace members of a set, which is not possible using the
* {@link CollectionPostMethodInvocationProcessor}. The downside is that whatever type of set was provided gets
* replaced with an {@code HashSet}.
*
* @author Tom Page
* @since 3.0
*/
public class SetPostMethodInvocationProcessor extends ModifiableCollectionPostMethodInvocationProcessor
{
@Override
protected Class<?> getClassName()
{
return Set.class;
}
@Override
protected <T> Collection<T> createEmptyCollection(Collection<T> collection)
{
return new HashSet<>();
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.interceptor.processor;
import java.util.Collection;
import java.util.SortedSet;
import java.util.TreeSet;
/**
* Sorted Set Post Method Invocation Processor. This replaces the existing set with a filtered {@link TreeSet}.
*
* @author Tom Page
* @since 3.0
*/
public class SortedSetPostMethodInvocationProcessor extends ModifiableCollectionPostMethodInvocationProcessor
{
@Override
protected Class<?> getClassName()
{
return SortedSet.class;
}
@Override
protected <T> Collection<T> createEmptyCollection(Collection<T> collection)
{
SortedSet<T> sortedSet = (SortedSet<T>) collection;
return new TreeSet<>(sortedSet.comparator());
}
}

View File

@@ -0,0 +1,137 @@
/*
* 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.interceptor.processor;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
/**
* Unit tests for the {@link CollectionPostMethodInvocationProcessor}.
*
* @author Tom Page
* @since 3.0
*/
public class CollectionPostMethodInvocationProcessorUnitTest
{
private static final String NON_FILTERED = "NON_FILTERED";
private static final String FILTERED = "FILTERED";
private static final String CHANGED_INPUT = "CHANGED_INPUT";
private static final String CHANGED_OUTPUT = "CHANGED_OUTPUT";
@InjectMocks CollectionPostMethodInvocationProcessor collectionPostMethodInvocationProcessor;
@Mock PostMethodInvocationProcessor mockPostMethodInvocationProcessor;
@Mock BasePostMethodInvocationProcessor mockStringProcessor;
@Before
public void setUp()
{
initMocks(this);
when(mockPostMethodInvocationProcessor.getProcessor(isA(String.class))).thenReturn(mockStringProcessor);
when(mockStringProcessor.process(NON_FILTERED)).thenReturn(NON_FILTERED);
when(mockStringProcessor.process(FILTERED)).thenReturn(null);
when(mockStringProcessor.process(CHANGED_INPUT)).thenReturn(CHANGED_OUTPUT);
}
@Test
public void testProcess_copesWithNull()
{
Object result = collectionPostMethodInvocationProcessor.process(null);
assertNull("Expected null collection to be passed through.", result);
}
@Test
public void testProcess_nullMember()
{
List<String> collection = new ArrayList<>();
collection.add(null);
Object result = collectionPostMethodInvocationProcessor.process(collection);
assertEquals("Expected collection containing null to be passed through.", collection, result);
}
@Test
public void testProcess_nonFilteredMember()
{
Object collection = Arrays.asList(NON_FILTERED);
Object result = collectionPostMethodInvocationProcessor.process(collection);
assertEquals("Expected element to still be present in result.", collection, result);
}
@Test
public void testProcess_filteredMemberInModifiableList()
{
List<String> collection = new ArrayList<>(Arrays.asList(FILTERED));
Collection<String> result = collectionPostMethodInvocationProcessor.process(collection);
assertTrue("Expected an empty list.", result.isEmpty());
}
@Test
public void testProcess_filteredMemberInUnmodifiableList()
{
List<String> collection = Arrays.asList(FILTERED, NON_FILTERED);
Collection<String> result = collectionPostMethodInvocationProcessor.process(collection);
assertNull("Since the collection could not be modified the whole thing should be filtered.", result);
}
@Test
public void testProcess_modifiedMember()
{
List<String> collection = Arrays.asList(NON_FILTERED, CHANGED_INPUT);
Collection<String> result = collectionPostMethodInvocationProcessor.process(collection);
assertNull("Since the Collection interface does not support replacement, the whole collection should be filtered.",
result);
}
@Test
public void testProcess_noProcessorDefined()
{
List<Integer> collection = Arrays.asList(1, 4, 91);
Collection<Integer> result = collectionPostMethodInvocationProcessor.process(collection);
assertEquals("If no processor is defined for the members then the whole list should be returned.", collection,
result);
}
}

View File

@@ -0,0 +1,112 @@
/*
* 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.interceptor.processor;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
/**
* Unit tests for {@link ListPostMethodInvocationProcessor}.
*
* @author Tom Page
* @since 3.0
*/
public class ListPostMethodInvocationProcessorUnitTest
{
private static final String NON_FILTERED = "NON_FILTERED";
private static final String FILTERED = "FILTERED";
private static final String CHANGED_INPUT = "CHANGED_INPUT";
private static final String CHANGED_OUTPUT = "CHANGED_OUTPUT";
@InjectMocks ListPostMethodInvocationProcessor listPostMethodInvocationProcessor;
@Mock PostMethodInvocationProcessor mockPostMethodInvocationProcessor;
@Mock BasePostMethodInvocationProcessor mockStringProcessor;
@Before
public void setUp()
{
initMocks(this);
when(mockPostMethodInvocationProcessor.getProcessor(isA(List.class))).thenReturn(listPostMethodInvocationProcessor);
when(mockPostMethodInvocationProcessor.getProcessor(isA(String.class))).thenReturn(mockStringProcessor);
when(mockStringProcessor.process(NON_FILTERED)).thenReturn(NON_FILTERED);
when(mockStringProcessor.process(FILTERED)).thenReturn(null);
when(mockStringProcessor.process(CHANGED_INPUT)).thenReturn(CHANGED_OUTPUT);
}
@Test
public void testProcessCollection_emptyList()
{
List<String> collection = new ArrayList<>();
Collection<String> result = listPostMethodInvocationProcessor.processCollection(collection, mockStringProcessor);
assertEquals(collection, result);
}
@Test
public void testProcessCollection_completelyFiltered()
{
List<String> collection = Arrays.asList(FILTERED, FILTERED);
Collection<String> result = listPostMethodInvocationProcessor.processCollection(collection, mockStringProcessor);
assertTrue("Expected all members of the list to be removed.", result.isEmpty());
}
@Test
public void testProcessCollection_supportsReplacement()
{
List<String> collection = Arrays.asList(NON_FILTERED, CHANGED_INPUT);
Collection<String> result = listPostMethodInvocationProcessor.processCollection(collection, mockStringProcessor);
List<String> expected = Arrays.asList(NON_FILTERED, CHANGED_OUTPUT);
assertEquals(expected, result);
}
@Test
public void testProcess_listOfLists()
{
List<String> innerListA = Arrays.asList(FILTERED, NON_FILTERED, CHANGED_INPUT);
List<String> innerListB = Arrays.asList(CHANGED_INPUT, FILTERED, NON_FILTERED);
List<List<String>> collection = Arrays.asList(innerListA, innerListB);
Collection<List<String>> result = listPostMethodInvocationProcessor.process(collection);
List<String> expectedInnerListA = Arrays.asList(NON_FILTERED, CHANGED_OUTPUT);
List<String> expectedInnerListB = Arrays.asList(CHANGED_OUTPUT, NON_FILTERED);
List<List<String>> expected = Arrays.asList(expectedInnerListA, expectedInnerListB);
assertEquals(expected, result);
}
}

View File

@@ -18,15 +18,10 @@
*/ */
package org.alfresco.module.org_alfresco_module_rm.classification.interceptor.processor; package org.alfresco.module.org_alfresco_module_rm.classification.interceptor.processor;
import static com.google.common.collect.ImmutableList.copyOf;
import static com.google.common.collect.Lists.newArrayList;
import static java.util.Collections.unmodifiableCollection;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static org.springframework.extensions.webscripts.GUID.generate; import static org.springframework.extensions.webscripts.GUID.generate;
import java.util.ArrayList;
import org.alfresco.module.org_alfresco_module_rm.classification.ContentClassificationService; import org.alfresco.module.org_alfresco_module_rm.classification.ContentClassificationService;
import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest; import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
@@ -90,83 +85,9 @@ public class NodeRefPostMethodInvocationProcessorUnitTest extends BaseUnitTest
} }
@Test @Test
public void testCollection_bothNodesConent_userClearedForBoth() public void testProcessingNull()
{ {
NodeRef nodeRef1 = generateNodeRef(); assertEquals("Expected null to be passed through without error.", null,
NodeRef nodeRef2 = generateNodeRef(); nodeRefPostMethodInvocationProcessor.process((NodeRef) null));
ArrayList<NodeRef> nodeRefs = newArrayList(nodeRef1, nodeRef2);
when(mockedDictionaryService.isSubClass(mockedNodeService.getType(nodeRef1), TYPE_CONTENT)).thenReturn(true);
when(mockedContentClassificationService.hasClearance(nodeRef1)).thenReturn(true);
when(mockedDictionaryService.isSubClass(mockedNodeService.getType(nodeRef2), TYPE_CONTENT)).thenReturn(true);
when(mockedContentClassificationService.hasClearance(nodeRef2)).thenReturn(true);
assertEquals(nodeRefs, nodeRefPostMethodInvocationProcessor.process(nodeRefs));
}
@Test
public void testCollection_bothNodesContent_userClearedForOne()
{
NodeRef nodeRef1 = generateNodeRef();
NodeRef nodeRef2 = generateNodeRef();
ArrayList<NodeRef> nodeRefs = newArrayList(nodeRef1, nodeRef2);
when(mockedDictionaryService.isSubClass(mockedNodeService.getType(nodeRef1), TYPE_CONTENT)).thenReturn(true);
when(mockedContentClassificationService.hasClearance(nodeRef1)).thenReturn(true);
when(mockedDictionaryService.isSubClass(mockedNodeService.getType(nodeRef2), TYPE_CONTENT)).thenReturn(true);
when(mockedContentClassificationService.hasClearance(nodeRef2)).thenReturn(false);
assertEquals(newArrayList(nodeRef1), nodeRefPostMethodInvocationProcessor.process(nodeRefs));
}
@SuppressWarnings("rawtypes")
@Test
public void testCollection_bothNodesContent_userClearedForNone()
{
NodeRef nodeRef1 = generateNodeRef();
NodeRef nodeRef2 = generateNodeRef();
ArrayList<NodeRef> nodeRefs = newArrayList(nodeRef1, nodeRef2);
when(mockedDictionaryService.isSubClass(mockedNodeService.getType(nodeRef1), TYPE_CONTENT)).thenReturn(true);
when(mockedContentClassificationService.hasClearance(nodeRef1)).thenReturn(false);
when(mockedDictionaryService.isSubClass(mockedNodeService.getType(nodeRef2), TYPE_CONTENT)).thenReturn(true);
when(mockedContentClassificationService.hasClearance(nodeRef2)).thenReturn(false);
assertEquals(new ArrayList(), nodeRefPostMethodInvocationProcessor.process(unmodifiableCollection(nodeRefs)));
}
@Test
public void testCollection_onlyOneNodeContent_userClearedForBoth()
{
NodeRef nodeRef1 = generateNodeRef();
NodeRef nodeRef2 = generateNodeRef();
ArrayList<NodeRef> nodeRefs = newArrayList(nodeRef1, nodeRef2);
when(mockedDictionaryService.isSubClass(mockedNodeService.getType(nodeRef1), TYPE_CONTENT)).thenReturn(false);
when(mockedContentClassificationService.hasClearance(nodeRef1)).thenReturn(true);
when(mockedDictionaryService.isSubClass(mockedNodeService.getType(nodeRef2), TYPE_CONTENT)).thenReturn(true);
when(mockedContentClassificationService.hasClearance(nodeRef2)).thenReturn(true);
assertEquals(nodeRefs, nodeRefPostMethodInvocationProcessor.process(copyOf(nodeRefs)));
}
@Test
public void testCollection_bothNodesNotContent_userClearedForBoth()
{
NodeRef nodeRef1 = generateNodeRef();
NodeRef nodeRef2 = generateNodeRef();
ArrayList<NodeRef> nodeRefs = newArrayList(nodeRef1, nodeRef2);
when(mockedDictionaryService.isSubClass(mockedNodeService.getType(nodeRef1), TYPE_CONTENT)).thenReturn(false);
when(mockedContentClassificationService.hasClearance(nodeRef1)).thenReturn(true);
when(mockedDictionaryService.isSubClass(mockedNodeService.getType(nodeRef2), TYPE_CONTENT)).thenReturn(false);
when(mockedContentClassificationService.hasClearance(nodeRef2)).thenReturn(true);
assertEquals(nodeRefs, nodeRefPostMethodInvocationProcessor.process(copyOf(nodeRefs)));
} }
} }

View File

@@ -0,0 +1,94 @@
/*
* 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.interceptor.processor;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.SortedSet;
import java.util.TreeSet;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
/**
* Unit tests for {@link SortedSetPostMethodInvocationProcessor}.
*
* @author Tom Page
* @since 3.0
*/
public class SortedSetPostMethodInvocationProcessorUnitTest
{
private static final String NON_FILTERED = "NON_FILTERED";
private static final String FILTERED = "FILTERED";
private static final String SHORT_INPUT = "SHORT_INPUT";
private static final String REALLY_LONG_OUTPUT_STRING = "REALLY_LONG_OUTPUT_STRING";
@InjectMocks SortedSetPostMethodInvocationProcessor sortedSetPostMethodInvocationProcessor;
@Mock BasePostMethodInvocationProcessor mockStringProcessor;
@Before
public void setUp()
{
initMocks(this);
when(mockStringProcessor.process(NON_FILTERED)).thenReturn(NON_FILTERED);
when(mockStringProcessor.process(FILTERED)).thenReturn(null);
when(mockStringProcessor.process(SHORT_INPUT)).thenReturn(REALLY_LONG_OUTPUT_STRING);
}
/**
* Given I have a sorted set of input strings
* When I pass it to the SortedSet processor
* Then I expect items above my clearance to be filtered
* And I expect items below my clearance to be passed through
* And I expect items that get changed by the filtering process to be changed
* And I expect the output set to be sorted using the same comparator as the input.
*/
@Test
public void testProcessCollection()
{
// Create a custom comparator that sorts based on the length of the strings.
Comparator<String> comparator = new Comparator<String>()
{
public int compare(String o1, String o2)
{
return o1.length() - o2.length();
}
};
SortedSet<String> collection = new TreeSet<>(comparator);
collection.add(SHORT_INPUT);
collection.add(NON_FILTERED);
collection.add(FILTERED);
Collection<String> result = sortedSetPostMethodInvocationProcessor.processCollection(collection, mockStringProcessor);
Iterator<String> iterator = result.iterator();
assertEquals("Expected the first element to be the shortest", NON_FILTERED, iterator.next());
assertEquals("Expected the second element to be the longest", REALLY_LONG_OUTPUT_STRING, iterator.next());
assertFalse("Expected two elements in output", iterator.hasNext());
}
}