From 5edb0f9b07dc28bf2261d6db6ade297c6e154606 Mon Sep 17 00:00:00 2001 From: Jean-Pierre Huynh Date: Wed, 2 Sep 2015 08:07:46 +0000 Subject: [PATCH 01/12] Created classified_renditions branch. (Based on Build #1861) git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/BRANCHES/classified_renditions@111256 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 From 06a7ef9b7306e4dedf2ab9ef1205b57e596472fd Mon Sep 17 00:00:00 2001 From: Neil McErlean Date: Wed, 2 Sep 2015 11:22:00 +0000 Subject: [PATCH 02/12] Common utility classes developed as part of refactor for RM-2549. asSet method that works like java.util.Arrays.asList. Also variants of java.util.Arrays.asList that take Supplier rather than T. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/BRANCHES/classified_renditions@111287 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../test/util/FPUtils.java | 84 +++++++++++++++++++ .../test/util/FPUtilsUnitTest.java | 66 +++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/FPUtils.java create mode 100644 rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/FPUtilsUnitTest.java diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/FPUtils.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/FPUtils.java new file mode 100644 index 0000000000..d0e49771f9 --- /dev/null +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/FPUtils.java @@ -0,0 +1,84 @@ +/* + * 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.test.util; + +import static java.util.stream.Collectors.toList; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Supplier; +import java.util.stream.Stream; + +/** + * Utility class to help with Java 8 FP stuff. + * + * @author Neil Mc Erlean + * @since 3.0.a + */ +public class FPUtils +{ + /** + * This method is intended to work exactly like {@code java.util.Arrays.asList()} but it takes + * a vararg of {@code Supplier}s instead of actual objects. + * + * @param suppliers a vararg of {@link Supplier}s giving a sequence of values for the list. + * @param the type of elements in the list. + * @return the list with each element retrieved from a {@code Supplier}. + */ + public static List asListFrom(Supplier... suppliers) + { + if (suppliers == null || suppliers.length == 0) + { + return Collections.emptyList(); + } + else + { + return Stream.of(suppliers) + .map(s -> s.get()) + .collect(toList()); + } + } + + public static Set asSetFrom(Supplier... suppliers) + { + List l = asListFrom(suppliers); + Set result = new HashSet<>(); + result.addAll(l); + return result; + } + + /** + * This utility method converts a vararg of objects into a Set. + * + * @param objects the objects to be added to the set + * @return a Set of objects (any equal objects will of course not be duplicated) + */ + public static Set asSet(T... objects) + { + Set result = new HashSet<>(); + for (T obj : objects) + { + result.add(obj); + } + + return result; + } +} \ No newline at end of file diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/FPUtilsUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/FPUtilsUnitTest.java new file mode 100644 index 0000000000..206687295a --- /dev/null +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/FPUtilsUnitTest.java @@ -0,0 +1,66 @@ +/* + * 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.test.util; + +import static org.alfresco.module.org_alfresco_module_rm.test.util.FPUtils.asListFrom; +import static org.alfresco.module.org_alfresco_module_rm.test.util.FPUtils.asSet; +import static org.junit.Assert.assertEquals; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.junit.Test; + +/** + * Unit tests for {@link FPUtils}. + * + * @author Neil Mc Erlean + * @since 3.0.a + */ +public class FPUtilsUnitTest +{ + @Test public void asListShouldProduceList() + { + List l = asListFrom(() -> "hello", + () -> "world", + () -> { + String s1 = "abc"; + String s2 = "xyz"; + return s1 + s2; + }); + assertEquals(asList("hello", "world", "abcxyz"), l); + } + + @Test public void asListShouldWorkForEmptyVarArgs() + { + assertEquals(emptyList(), FPUtils.asListFrom()); + } + + @Test public void asSetShouldProduceSet() + { + Set expectedSet = new HashSet<>(); + expectedSet.add("hello"); + expectedSet.add("world"); + + assertEquals(expectedSet, asSet("hello", "hello", "world")); + } +} \ No newline at end of file From 9e4ccdd0537e70bd8d3bb01e77ff8ee2550bfa30 Mon Sep 17 00:00:00 2001 From: Neil McErlean Date: Wed, 2 Sep 2015 12:03:38 +0000 Subject: [PATCH 03/12] This checkin provides the non-RM-specific parts of metadata delegation, which are required for the refactor of classified renditions needed for RM-2549. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We essentially have two new services, MetadataDelegationService and MetadataDelegationAdminService, along with some spring configuration and content model configuration (this will come in the subsequent RM-specific checkin.) metadata-delegation-context.xml defines the spring beans that form the Metadata Delegation services. The DelegationAdminService is used to attach/detach nodes in pairs such that one node can ‘inherit’ some aspect metadata from another node - the ‘delegate’. The Delegation class is the definition of a type of link - it’s defined by the aspects that it handles and the type of peer association that it uses to link pairs of nodes. Delegations must be defined as spring beans in the system to be available for use and they are exposed to Java code via the DelegationRegistry class. Note that chains of delegations (node A -> node B -> node C) are not supported. Any attempt to attach nodes in a way that would lead to a delegation chain is detected and prevented. The DelegationService is not about the creation and destruction of links, but is about accessing the metadata on a delegate node. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/BRANCHES/classified_renditions@111292 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../metadata-delegation-context.xml | 123 +++++++++++ .../org_alfresco_module_rm/module-context.xml | 5 +- .../metadatadelegation/Delegation.java | 135 ++++++++++++ .../DelegationAdminService.java | 92 +++++++++ .../DelegationAdminServiceImpl.java | 167 +++++++++++++++ .../DelegationException.java | 107 ++++++++++ .../DelegationRegistry.java | 85 ++++++++ .../metadatadelegation/DelegationService.java | 96 +++++++++ .../DelegationServiceImpl.java | 182 ++++++++++++++++ .../metadatadelegation/package-info.java | 46 +++++ .../DelegationAdminServiceImplUnitTest.java | 141 +++++++++++++ .../DelegationServiceImplUnitTest.java | 194 ++++++++++++++++++ .../DelegationUnitTest.java | 128 ++++++++++++ 13 files changed, 1500 insertions(+), 1 deletion(-) create mode 100644 rm-server/config/alfresco/module/org_alfresco_module_rm/metadata-delegation-context.xml create mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/Delegation.java create mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationAdminService.java create mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationAdminServiceImpl.java create mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationException.java create mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationRegistry.java create mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationService.java create mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationServiceImpl.java create mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/package-info.java create mode 100644 rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationAdminServiceImplUnitTest.java create mode 100644 rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationServiceImplUnitTest.java create mode 100644 rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationUnitTest.java diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/metadata-delegation-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/metadata-delegation-context.xml new file mode 100644 index 0000000000..106cf0cb90 --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/metadata-delegation-context.xml @@ -0,0 +1,123 @@ + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationAdminService + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + + org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationAdminService.attachDelegate=ACL_ALLOW + org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationAdminService.detachDelegate=ACL_ALLOW + org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationAdminService.getDefinedDelegations=ACL_ALLOW + org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationAdminService.getDelegationFor=ACL_ALLOW + org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationAdminService.getDelegationsFrom=ACL_ALLOW + org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationAdminService.*=ACL_DENY + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationService + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + + org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationService.hasDelegateForAspect=ACL_ALLOW + org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationService.getDelegateFor=ACL_ALLOW + org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationService.getDelegateProperties=ACL_ALLOW + org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationService.getDelegateProperty=ACL_ALLOW + org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationService.hasAspectOnDelegate=ACL_ALLOW + org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationService.getDelegations=ACL_ALLOW + org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationService.*=ACL_DENY + + + + + + + + + + + + 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 f75a9ff12d..921a34aaf0 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 @@ -267,9 +267,12 @@ + + + - + diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/Delegation.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/Delegation.java new file mode 100644 index 0000000000..05a2e3caef --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/Delegation.java @@ -0,0 +1,135 @@ +/* + * 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.metadatadelegation; + +import org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationException.InvalidDelegation; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.QName; + +import java.util.Collections; +import java.util.Objects; +import java.util.Set; + +/** + * A Delegation is a definition of a {@link #aspects set of aspects} whose metadata are to be delegated. + * Using a Delegation, you can attach a delegate node to any node and {@code hasAspect} and + * {@code getPropert[y|ies]} calls can be delegated to the delegate node. + *

+ * The connection between the nodes is made with a specified {@link #assocType peer association}. + *

+ * Note that a Delegation is not an instance of a link between two nodes, but the definition of such a link. + * + * @author Neil Mc Erlean + * @since 3.0.a + */ +public class Delegation +{ + private DictionaryService dictionaryService; + private DelegationRegistry delegationRegistry; + private Set aspects; + private QName assocType; + + public Delegation() + { + // Intentionally empty + } + + public void setDictionaryService(DictionaryService service) + { + this.dictionaryService = service; + } + + public void setDelegationRegistry(DelegationRegistry registry) + { + this.delegationRegistry = registry; + } + + public void setAssocType(QName assocType) + { + this.assocType = assocType; + } + + public void setAspects(Set aspects) + { + this.aspects = aspects; + } + + public void validateAndRegister() + { + if (this.assocType == null) + { + throw new InvalidDelegation("Illegal null assocType"); + } + if (aspects == null || aspects.isEmpty()) + { + throw new InvalidDelegation("Illegal null or empty aspects set"); + } + if (dictionaryService.getAssociation(assocType) == null) + { + throw new InvalidDelegation("Association not found: " + assocType); + } + for (QName aspect : aspects) + { + if (dictionaryService.getAspect(aspect) == null) + { + throw new InvalidDelegation("Aspect not found: " + aspect); + } + } + + this.delegationRegistry.register(this); + } + + /** Gets the type of the peer association linking the node to its delegate. */ + public QName getAssocType() + { + return assocType; + } + + /** Gets the set of aspects which are being delegated. */ + public Set getAspects() + { + return Collections.unmodifiableSet(aspects); + } + + @Override public int hashCode() + { + return Objects.hash(aspects, assocType); + } + + @Override public boolean equals(Object other) + { + boolean result = false; + if (other instanceof Delegation) + { + Delegation that = (Delegation)other; + result = this.aspects.equals(that.aspects) && + this.assocType.equals(that.assocType); + } + return result; + } + + @Override public String toString() + { + StringBuilder result = new StringBuilder(); + result.append(this.getClass().getSimpleName()).append(':') + .append("--").append(assocType).append("->") + .append(aspects); + return result.toString(); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationAdminService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationAdminService.java new file mode 100644 index 0000000000..0a49ce4d2c --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationAdminService.java @@ -0,0 +1,92 @@ +/* + * 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.metadatadelegation; + +import static org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationException.ChainedDelegationUnsupported; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +import java.util.Set; + +/** + * A service to manage the delegation of aspect metadata. + * Using this service a node can be {@link #attachDelegate linked} to a delegate node for a configured set of aspects. + * (Note that this delegate node must already exist within the database.) + * Then any read request for relevant metadata such as hasAspect or getProperties can be delegated to the + * linked node. + *

+ * For a link to be made, there must be a {@link #getDefinedDelegations() defined Delegation} already in the system. + * This means that a peer-association type will have to have been declared and that a spring bean will have to have + * defined which aspects are to be handled by this {@code Delegation}. + * + * @author Neil Mc Erlean + * @since 3.0.a + */ +public interface DelegationAdminService +{ + /** + * Creates a link between two nodes such that the first {@code nodeRef} can 'inherit' or reuse some aspect + * metadata from another node - the {@code delegateNodeRef}. + *

+ * Note that links can currently only extend between two pairs of nodes and cannot be chained. + * + * @param nodeRef the node which is to inherit additional metadata. + * @param delegateNodeRef the node which will provide the additional metadata. + * @param assocType the type of the peer association which will link the two nodes. + * @return a {@link Delegation} object which defines the link type. + * @throws ChainedDelegationUnsupported if an attempt is made to attach nodes such that a chain would be made. + */ + Delegation attachDelegate(NodeRef nodeRef, NodeRef delegateNodeRef, QName assocType); + + /** + * Removes an existing metadata delegation link between two nodes. + * + * @param nodeRef the node which has been linked to a delegate. + * @param assocType the type of the peer assocation forming the link. + * @return the removed {@link Delegation}. + */ + Delegation detachDelegate(NodeRef nodeRef, QName assocType); + + /** + * Gets the set of defined {@link Delegation}s. + * + * @return the set of defined Delegations. + */ + Set getDefinedDelegations(); + + /** + * Gets the {@link Delegation} which contains the specified {@code aspectName} if there is one. + * Note that only one {@link Delegation} may declare that it handles any particular aspect. + * + * @param aspectName the name of the aspect whose {@link Delegation} is sought. + * @return the {@link Delegation} which handles the specified aspect, if there is one. + */ + Delegation getDelegationFor(QName aspectName); + + /** + * Gets the set of {@link Delegation}s which are in effect from the specified {@code nodeRef}. + * From these, you can retrieve the types of peer associations which are linked to the specified + * {@code nodeRef} as well as the aspect types that are handled. + * + * @param nodeRef the NodeRef whose delegations are sought. + * @return the set of {@link Delegation}s from the specified nodeRef. + */ + Set getDelegationsFrom(NodeRef nodeRef); +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationAdminServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationAdminServiceImpl.java new file mode 100644 index 0000000000..0a2463ba05 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationAdminServiceImpl.java @@ -0,0 +1,167 @@ +/* + * 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.metadatadelegation; + +import static org.alfresco.util.collections.CollectionUtils.transform; + +import org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationException.ChainedDelegationUnsupported; +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.collections.Function; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @author Neil Mc Erlean + * @since 3.0.a + */ +public class DelegationAdminServiceImpl implements DelegationAdminService +{ + private DelegationRegistry registry; + private NodeService nodeService; + + public void setNodeService(NodeService service) + { + this.nodeService = service; + } + + public void setDelegationRegistry(DelegationRegistry registry) + { + this.registry = registry; + } + + @Override public Delegation attachDelegate(NodeRef nodeRef, NodeRef delegateNodeRef, QName assocType) + { + final Delegation delegation = getDelegationForAssociation(assocType); + + // Prevent the creation of chains of delegation from node A to B to C. + + // If any nodes are already delegating to nodeRef for the specified assoc, then we can't chain. + final List existingDelegatorAssocs = nodeService.getSourceAssocs(nodeRef, assocType); + if ( !existingDelegatorAssocs.isEmpty()) + { + final List existingDelegators = transform(existingDelegatorAssocs, + new Function() + { + @Override public NodeRef apply(AssociationRef assocRef) + { + return assocRef.getSourceRef(); + } + }); + throw new ChainedDelegationUnsupported("Cannot attach delegate", existingDelegators); + } + + // Likewise if this delegate node is already itself delegating elsewhere, we cannot chain. + final List existingDelegateAssocs = nodeService.getTargetAssocs(delegateNodeRef, assocType); + if ( !existingDelegateAssocs.isEmpty()) + { + // If it's not empty, it should only have one value in it, but just in case... + final List existingDelegates = transform(existingDelegateAssocs, + new Function() + { + @Override public NodeRef apply(AssociationRef assocRef) + { + return assocRef.getTargetRef(); + } + }); + throw new ChainedDelegationUnsupported("Cannot attach delegate", existingDelegates); + } + + // OK. We're good to go. We're not making a chain here. + nodeService.createAssociation(nodeRef, delegateNodeRef, assocType); + + return delegation; + } + + private Delegation getDelegationForAssociation(QName assocType) + { + final Delegation delegation = registry.getDelegateForAssociation(assocType); + + if (delegation == null) + { + throw new IllegalArgumentException("No " + Delegation.class.getSimpleName() + + " configured for assocType " + assocType); + } + return delegation; + } + + @Override public Delegation detachDelegate(NodeRef nodeRef, QName assocType) + { + // Is the association there? + final List assocs = nodeService.getTargetAssocs(nodeRef, assocType); + + if (assocs == null || assocs.isEmpty()) + { + return null; + } + else + { + Delegation result = getDelegationForAssociation(assocType); + + // There should only be one such association... but we'll remove them all just in case + for (AssociationRef assocRef : assocs) + { + nodeService.removeAssociation(nodeRef, assocRef.getTargetRef(), assocType); + } + + return result; + } + } + + @Override public Delegation getDelegationFor(QName aspectName) + { + Delegation delegation = null; + + for (Delegation d : getDefinedDelegations()) + { + if (d.getAspects().contains(aspectName)) + { + delegation = d; + break; + } + } + return delegation; + } + + @Override public Set getDelegationsFrom(NodeRef nodeRef) + { + final Set allDelegations = getDefinedDelegations(); + + final Set result = new HashSet<>(); + for (Delegation d : allDelegations) + { + final QName assocType = d.getAssocType(); + if ( !nodeService.getTargetAssocs(nodeRef, assocType).isEmpty()) + { + result.add(d); + } + } + + return result; + } + + @Override public Set getDefinedDelegations() + { + return registry.getDelegations(); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationException.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationException.java new file mode 100644 index 0000000000..ea978abf7b --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationException.java @@ -0,0 +1,107 @@ +/* + * 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.metadatadelegation; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.service.cmr.repository.NodeRef; + +import java.util.List; + +/** + * Generic class for any runtime exceptions related to metadata delegates. + * + * @author Neil Mc Erlean + * @since 3.0.a + */ +public class DelegationException extends AlfrescoRuntimeException +{ + public DelegationException(String msgId) { super(msgId); } + public DelegationException(String msgId, Throwable cause) { super(msgId, cause); } + + public static class InvalidDelegation extends DelegationException + { + public InvalidDelegation(String msgId) + { + super(msgId); + } + } + + /** A Metadata Delegation already exists. */ + public static class DelegationAlreadyExists extends DelegationException + { + private final Delegation delegation; + + public DelegationAlreadyExists(String msgId, Delegation delegation) + { + super(msgId); + this.delegation = delegation; + } + } + + /** + * A {@link Delegation} has not been found. + * Remember that a Delegation is the definition of a type of link. + */ + public static class DelegationNotFound extends DelegationException + { + public DelegationNotFound(String msgId) + { + super(msgId); + } + } + + /** + * A Delegate has not been found. + * Remember that a Delegate is an instance of a link between two nodes. + */ + public static class DelegateNotFound extends DelegationException + { + public DelegateNotFound(String msgId) + { + super(msgId); + } + } + + /** + * Exception to report that we currently do not support chained delegation. + */ + public static class ChainedDelegationUnsupported extends DelegationException + { + private final List nodesAlreadyDelegating; + + public ChainedDelegationUnsupported(String msgId, List nodesAlreadyDelegating) + { + super(msgId); + this.nodesAlreadyDelegating = nodesAlreadyDelegating; + } + + public List getNodesAlreadyDelegating() + { + return this.nodesAlreadyDelegating; + } + + @Override public String toString() + { + StringBuilder msg = new StringBuilder(); + msg.append(this.getClass().getSimpleName()).append(" Already delegating from: ") + .append(nodesAlreadyDelegating.toString()); + return msg.toString(); + } + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationRegistry.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationRegistry.java new file mode 100644 index 0000000000..54133ee40b --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationRegistry.java @@ -0,0 +1,85 @@ +/* + * 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.metadatadelegation; + +import org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationException.DelegationAlreadyExists; +import org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationException.InvalidDelegation; +import org.alfresco.service.namespace.QName; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * This is a registry of {@link Delegation delegations} which have been defined in the system. + * + * @author Neil Mc Erlean + * @since 3.0.a + */ +public class DelegationRegistry +{ + private final Set delegations = new HashSet<>(); + + public void register(Delegation delegation) + { + // Various validation steps to do here to ensure we get consistent, sensible Delegations registered. + if (delegations.contains(delegation)) + { + throw new DelegationAlreadyExists("Cannot register duplicate delegation", delegation); + } + for (Delegation existingDelegation : delegations) + { + if (existingDelegation.getAssocType().equals(delegation.getAssocType())) + { + throw new InvalidDelegation("Cannot register two delegations with the same assocType. " + + "Existing: " + existingDelegation + + " New: " + delegation); + } + // Yes this is a for loop inside a for loop but we're assuming these sets will not be large. + for (QName existingAspect : existingDelegation.getAspects()) + { + if (delegation.getAspects().contains(existingAspect)) + { + throw new InvalidDelegation("Cannot register two delegations with the same aspect. " + + "Existing: " + existingDelegation + + " New: " + delegation); + } + } + } + + this.delegations.add(delegation); + } + + public Set getDelegations() + { + return Collections.unmodifiableSet(delegations); + } + + public Delegation getDelegateForAssociation(QName assocType) + { + for (Delegation d : delegations) + { + if (d.getAssocType().equals(assocType)) + { + return d; + } + } + return null; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationService.java new file mode 100644 index 0000000000..b4048761c0 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationService.java @@ -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 . + */ +package org.alfresco.module.org_alfresco_module_rm.metadatadelegation; + +import static org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationException.DelegationNotFound; + +import org.alfresco.service.cmr.repository.InvalidNodeRefException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +import java.io.Serializable; +import java.util.Map; + +/** + * This service provides read-only access to delegated metadata. + * TODO complete. + * + * @author Neil Mc Erlean + * @since 3.0.a + */ +public interface DelegationService +{ + /** + * Checks if the specified nodeRef has an attached {@link Delegation} for the specified aspect. + * + * @param nodeRef the nodeRef which may or may not have a delegate node for the specified aspect. + * @param aspectName the name of the aspect for which the node may or may not have delegation. + * @return whether the node is delegating metadata reads for the specified aspect. + * @throws InvalidNodeRefException if the supplied nodeRef does not exist. + * @throws DelegationNotFound if no delegation for the specified aspect has been attached. + */ + boolean hasDelegateForAspect(NodeRef nodeRef, QName aspectName); + + /** + * Gets the delegate node for the specified aspect, if there is one. + * + * @param nodeRef the node with the delegate. + * @param aspectName the aspect name. + * @return the nodeRef of the delegate if there is one, else {@code null}. + * @throws DelegationNotFound if no delegation for the specified aspect has been attached. + */ + NodeRef getDelegateFor(NodeRef nodeRef, QName aspectName); + + /** + * Gets all the property values from the delegate node for the specified aspect. + * + * @param nodeRef the node with the delegate. + * @param aspectName the aspect name which holds the properties we want. + * @return the property values as obtained from the delegate node. + */ + Map getDelegateProperties(NodeRef nodeRef, QName aspectName); + + /** + * Gets the specified property value from the delegate node. + * + * @param nodeRef the node with the delegate. + * @param propertyName the property name which we want. + * @return the property value as obtained from the delegate node. + */ + Serializable getDelegateProperty(NodeRef nodeRef, QName propertyName); + + /** + * Determines if a given aspect is present on a node's delegates. + * + * @param nodeRef the node for which a delegate is sought. + * @param aspectName the aspect which is to be checked. + * @return Returns true if the aspect has been applied to one of the given node's delegates, + * otherwise false + */ + boolean hasAspectOnDelegate(NodeRef nodeRef, QName aspectName); + + /** + * Gets all {@link Delegation delegations} currently attached to the specified node. + * + * @param nodeRef the node whose delegations are sought. + * @return Returns a map of all {@link Delegation delegations} by NodeRef for the specified nodeRef. + */ + Map getDelegations(NodeRef nodeRef); +} + diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationServiceImpl.java new file mode 100644 index 0000000000..3b7190c7d5 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationServiceImpl.java @@ -0,0 +1,182 @@ +/* + * 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.metadatadelegation; + +import static org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationException.DelegateNotFound; +import static org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationException.DelegationNotFound; +import static org.alfresco.util.collections.CollectionUtils.filterKeys; + +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.InvalidNodeRefException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.collections.Function; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @author Neil Mc Erlean + * @since 3.0.a + */ +public class DelegationServiceImpl implements DelegationService +{ + private DelegationAdminService delegationAdminService; + private DictionaryService dictionaryService; + private NodeService nodeService; + + public void setDelegationAdminService(DelegationAdminService service) + { + this.delegationAdminService = service; + } + + public void setDictionaryService(DictionaryService service) + { + this.dictionaryService = service; + } + + public void setNodeService(NodeService service) + { + this.nodeService = service; + } + + @Override public boolean hasDelegateForAspect(NodeRef nodeRef, QName aspectName) + { + final Delegation delegation = delegationAdminService.getDelegationFor(aspectName); + + if ( !nodeService.exists(nodeRef)) + { + throw new InvalidNodeRefException(nodeRef); + } + else if (delegation == null) + { + throw new DelegationNotFound("No delegation found for aspect: " + aspectName); + } + else + { + final List targetAssocs = nodeService.getTargetAssocs(nodeRef, delegation.getAssocType()); + return !targetAssocs.isEmpty(); + } + } + + @Override public NodeRef getDelegateFor(NodeRef nodeRef, QName aspectName) + { + final Delegation d = delegationAdminService.getDelegationFor(aspectName); + + if (d == null) + { + throw new DelegationNotFound("No delegation found for aspect: " + aspectName); + } + else + { + final QName assocType = d.getAssocType(); + final List assocs = nodeService.getTargetAssocs(nodeRef, assocType); + + return assocs.isEmpty() ? null : assocs.get(0).getTargetRef(); + } + } + + @Override public Map getDelegateProperties(NodeRef nodeRef, final QName aspectName) + { + final NodeRef delegateNode = getDelegateFor(nodeRef, aspectName); + + if (delegateNode == null) + { + throw new DelegateNotFound("No delegate node found for " + nodeRef + " " + aspectName); + } + else + { + Map allProps = nodeService.getProperties(delegateNode); + Map aspectProps = filterKeys(allProps, + new Function() + { + @Override public Boolean apply(QName propName) + { + final QName containerClassname = dictionaryService.getProperty(propName) + .getContainerClass() + .getName(); + return containerClassname.equals(aspectName); + } + }); + return aspectProps; + } + } + + @Override public Serializable getDelegateProperty(NodeRef nodeRef, QName propertyName) + { + final PropertyDefinition propDefn = dictionaryService.getProperty(propertyName); + + if (propDefn == null) + { + throw new IllegalArgumentException("Property " + propertyName + " not found."); + } + + final ClassDefinition aspectDefn = propDefn.getContainerClass(); + if (!aspectDefn.isAspect()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Property '").append(propertyName).append("' is not defined on an aspect: ") + .append(aspectDefn.getName()); + + throw new IllegalArgumentException(msg.toString()); + } + + Map allPropValues = getDelegateProperties(nodeRef, aspectDefn.getName()); + return allPropValues.get(propertyName); + } + + @Override public boolean hasAspectOnDelegate(NodeRef nodeRef, QName aspectName) + { + final NodeRef delegateNode = getDelegateFor(nodeRef, aspectName); + + if (delegateNode == null) + { + throw new DelegateNotFound("No delegate node found for " + nodeRef + " " + aspectName); + } + else + { + return nodeService.hasAspect(delegateNode, aspectName); + } + } + + @Override public Map getDelegations(NodeRef nodeRef) + { + Set delegations = delegationAdminService.getDelegationsFrom(nodeRef); + + Map result = new HashMap<>(); + for (Delegation d : delegations) + { + // We need only use the first aspect to get the Delegation object + if (!d.getAspects().isEmpty()) + { + result.put(d, getDelegateFor(nodeRef, d.getAspects().iterator().next())); + } + } + + return result; + } +} + diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/package-info.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/package-info.java new file mode 100644 index 0000000000..b35ca04e74 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/package-info.java @@ -0,0 +1,46 @@ +/* + * 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 types that deliver the Metadata Delegation feature. + * Metadata delegation allows read-only aspect metadata for any given Alfresco node to + * be sourced from another node, the delegate. + *

+ * In this way nodes can 'inherit' some of their metadata from another node which may + * have benefits when more than one node is required to share some of the same metadata. + *

+ * Multiple nodes may share the same delegate node and one node may be linked to multiple + * delegates. + *

+ * The linking of nodes to their metadata delegates is done with Alfresco peer associations. + * Association types must be declared in an Alfresco content model in the normal way. + * Spring configuration is used to assign each association type a set of aspects which will + * be available from the delegate via the association. + *

+ * See {@link org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationAdminService} + * for details on how to create and destroy delegation links between nodes. + *

+ * The read-only access to delegated metadat is made available via the + * See {@link org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationService} + * for details on how the data access is performed. + *

+ * See {@link org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationRegistry} + * for details on what {@link org.alfresco.module.org_alfresco_module_rm.metadatadelegation.Delegation}s + * are defined in the system. + */ +package org.alfresco.module.org_alfresco_module_rm.metadatadelegation; \ No newline at end of file diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationAdminServiceImplUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationAdminServiceImplUnitTest.java new file mode 100644 index 0000000000..1cd6a81caf --- /dev/null +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationAdminServiceImplUnitTest.java @@ -0,0 +1,141 @@ +/* + * 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.metadatadelegation; + +import static java.util.Arrays.asList; +import static org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationException.ChainedDelegationUnsupported; +import static org.alfresco.module.org_alfresco_module_rm.test.util.ExceptionUtils.expectedException; +import static org.alfresco.module.org_alfresco_module_rm.test.util.FPUtils.asSet; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; + +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.QName; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Unit tests for {@link DelegationAdminServiceImpl}. + * + * @author Neil Mc Erlean + * @since 3.0.a + */ +public class DelegationAdminServiceImplUnitTest +{ + @InjectMocks private final DelegationAdminServiceImpl delegationAdminService = new DelegationAdminServiceImpl(); + + @Mock DictionaryService mockDictionaryService; + @Mock NodeService mockNodeService; + @Mock DelegationRegistry mockRegistry; + @Mock DelegationServiceImpl mockDelegationService; + + private final NodeRef node1 = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "node1"); + private final NodeRef node2 = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "node2"); + private final NodeRef node3 = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "node3"); + + private final QName assoc1 = QName.createQName("test", "assoc1"); + private final QName assoc2 = QName.createQName("test", "assoc2"); + + private final QName aspect1 = QName.createQName("test", "aspect1"); + private final QName aspect2 = QName.createQName("test", "aspect2"); + private final QName aspect3 = QName.createQName("test", "aspect3"); + + private final Delegation delegate1 = new Delegation() + {{ + this.setAssocType(assoc1); + this.setAspects(asSet(aspect1, aspect2)); + }}; + private final Delegation delegate2 = new Delegation() + {{ + this.setAssocType(assoc2); + this.setAspects(asSet(aspect3)); + }}; + + @Before public void setUp() + { + MockitoAnnotations.initMocks(this); + + when(mockRegistry.getDelegations()).thenReturn(asSet(delegate1, delegate2)); + } + + @Test(expected=IllegalArgumentException.class) + public void attachingDelegateWithNoAssociationConfiguredShouldFail() + { + delegationAdminService.attachDelegate(node1, node2, assoc1); + } + + @Test public void attachDetach() + { + when(mockRegistry.getDelegateForAssociation(assoc1)).thenReturn(delegate1); + + // attach + Delegation d = attachDelegate(node1, node2, assoc1); + + // validate + assertEquals(assoc1, d.getAssocType()); + assertEquals(asSet(aspect1, aspect2), d.getAspects()); + assertTrue(mockDelegationService.hasDelegateForAspect(node1, aspect1)); + assertFalse(mockDelegationService.hasDelegateForAspect(node1, aspect3)); + + // detach + assertEquals(d, delegationAdminService.detachDelegate(node1, assoc1)); + } + + private Delegation attachDelegate(NodeRef from, NodeRef to, QName assocType) + { + Delegation d = delegationAdminService.attachDelegate(from, to, assocType); + when(mockNodeService.getSourceAssocs(to, assocType)).thenReturn(asList(new AssociationRef(from, assocType, to))); + when(mockNodeService.getTargetAssocs(from, assocType)).thenReturn(asList(new AssociationRef(from, assocType, to))); + for (QName aspect : d.getAspects()) + { + when(mockDelegationService.hasDelegateForAspect(from, aspect)).thenReturn(true); + } + return d; + } + + @Test public void chainsOfDelegationShouldBePrevented() + { + when(mockRegistry.getDelegateForAssociation(assoc1)).thenReturn(delegate1); + + // The node already has a delegation in place: node1 -> node2. We're trying to add to the + // end of the chain: node2 -> node3 + when(mockNodeService.getSourceAssocs(node2, assoc1)).thenReturn(asList(new AssociationRef(node1, assoc1, node2))); + when(mockNodeService.getTargetAssocs(node1, assoc1)).thenReturn(asList(new AssociationRef(node1, assoc1, node2))); + + expectedException(ChainedDelegationUnsupported.class, () -> { + delegationAdminService.attachDelegate(node2, node3, assoc1); + return null; + }); + + // Now try to add to the start of the chain: node3 -> node1 + expectedException(ChainedDelegationUnsupported.class, () -> { + delegationAdminService.attachDelegate(node3, node1, assoc1); + return null; + }); + } +} diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationServiceImplUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationServiceImplUnitTest.java new file mode 100644 index 0000000000..b28fae5de4 --- /dev/null +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationServiceImplUnitTest.java @@ -0,0 +1,194 @@ +/* + * 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.metadatadelegation; + +import static java.util.Collections.emptyMap; +import static java.util.Arrays.asList; +import static org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationException.DelegateNotFound; +import static org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationException.DelegationNotFound; +import static org.alfresco.module.org_alfresco_module_rm.test.util.ExceptionUtils.expectedException; +import static org.alfresco.module.org_alfresco_module_rm.test.util.FPUtils.asSet; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.when; + +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.QName; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +/** + * Unit tests for {@link DelegationServiceImpl}. + * + * @author Neil Mc Erlean + * @since 3.0.a + */ +public class DelegationServiceImplUnitTest +{ + @InjectMocks private final DelegationServiceImpl delegationService = new DelegationServiceImpl(); + + @Mock DictionaryService mockDictionaryService; + @Mock NodeService mockNodeService; + @Mock DelegationAdminServiceImpl mockDelegationAdminService; + + /** This node has a delegate node. */ + private final NodeRef nodeWithDelegate = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "nodeWithDelegate"); + /** This is the delgate for {@link #nodeWithDelegate}. */ + private final NodeRef delegateNode = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "delegateNode"); + /** This node has no delegate node. */ + private final NodeRef nodeWithoutDelegate = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "nodeWithoutDelegate"); + + /** The type of the peer association that links the delegate to its source. */ + private final QName delegateAssocType = QName.createQName("test", "delegateAssocType"); + /** The instance of the association between {@link #nodeWithDelegate} and {@link #delegateNode}. */ + private final AssociationRef delegateAssocRef = new AssociationRef(nodeWithDelegate, delegateAssocType, delegateNode); + + /** Name of an aspect that has been delegated. */ + private final QName delegatedAspect1 = QName.createQName("test", "delegatedAspect1"); + /** Name of an aspect that has been delegated. */ + private final QName delegatedAspect2 = QName.createQName("test", "delegatedAspect2"); + /** Name of a content class (a type in this case) that has not been delegated. + * N.B. Types can't be delegated currently. */ + private final QName undelegatedType = QName.createQName("test", "undelegatedType"); + + private final QName delegatedProp = QName.createQName("test", "delegatedProp"); + private final Serializable delegatedPropValue = "hello"; + private final QName undelegatedProp = QName.createQName("test", "undelegatedProp"); + + private final Delegation delegate = new Delegation() + {{ + this.setAssocType(delegateAssocType); + this.setAspects(asSet(delegatedAspect1, delegatedAspect2)); + }}; + + @Before public void setUp() + { + MockitoAnnotations.initMocks(this); + + final PropertyDefinition aspectProp = mock(PropertyDefinition.class); + final ClassDefinition aspectDefn = mock(ClassDefinition.class); + when(aspectDefn.getName()).thenReturn(delegatedAspect1); + when(aspectProp.getContainerClass()).thenReturn(aspectDefn); + when(aspectDefn.isAspect()).thenReturn(true); + + final PropertyDefinition typeProp = mock(PropertyDefinition.class); + final ClassDefinition typeDefn = mock(ClassDefinition.class); + when(typeDefn.getName()).thenReturn(undelegatedType); + when(typeProp.getContainerClass()).thenReturn(typeDefn); + when(typeDefn.isAspect()).thenReturn(false); + + when(mockDictionaryService.getProperty(delegatedProp)).thenReturn(aspectProp); + + when(mockDelegationAdminService.getDelegationsFrom(nodeWithDelegate)).thenReturn(asSet(delegate)); + for (QName delegatedAspect : asSet(delegatedAspect1, delegatedAspect2)) + { + when(mockDelegationAdminService.getDelegationFor(delegatedAspect)).thenReturn(delegate); + when(mockNodeService.hasAspect(delegateNode, delegatedAspect)).thenReturn(true); + } + when(mockNodeService.getSourceAssocs(delegateNode, delegateAssocType)).thenReturn(asList(delegateAssocRef)); + when(mockNodeService.getTargetAssocs(nodeWithDelegate, delegateAssocType)).thenReturn(asList(delegateAssocRef)); + when(mockNodeService.exists(any(NodeRef.class))).thenReturn(true); + when(mockNodeService.getProperties(delegateNode)) + .thenReturn(new HashMap() + {{ + this.put(delegatedProp, delegatedPropValue); + }}); + } + + @Test public void hasDelegateForAspect() + { + assertTrue(delegationService.hasDelegateForAspect(nodeWithDelegate, delegatedAspect1)); + expectedException(DelegationNotFound.class, () -> delegationService.hasDelegateForAspect(nodeWithoutDelegate, undelegatedType)); + assertFalse(delegationService.hasDelegateForAspect(nodeWithoutDelegate, delegatedAspect1)); + } + + @Test public void getDelegateFor() + { + assertEquals(delegateNode, delegationService.getDelegateFor(nodeWithDelegate, delegatedAspect1)); + expectedException(DelegationNotFound.class, () -> + { + delegationService.getDelegateFor(nodeWithDelegate, undelegatedType); + return null; + }); + assertNull(delegationService.getDelegateFor(nodeWithoutDelegate, delegatedAspect1)); + } + + @Test public void getDelegateProperties() + { + final Map expectedProps = new HashMap<>(); + expectedProps.put(delegatedProp, delegatedPropValue); + + assertEquals(expectedProps, delegationService.getDelegateProperties(nodeWithDelegate, delegatedAspect1)); + + expectedException(DelegationNotFound.class, + () -> delegationService.getDelegateProperties(nodeWithDelegate, undelegatedType)); + + expectedException(DelegateNotFound.class, + () -> delegationService.getDelegateProperties(nodeWithoutDelegate, delegatedAspect1)); + } + + @Test public void getDelegateProperty() + { + assertEquals(delegatedPropValue, delegationService.getDelegateProperty(nodeWithDelegate, delegatedProp)); + + expectedException(IllegalArgumentException.class, + () -> delegationService.getDelegateProperty(nodeWithDelegate, undelegatedProp)); + + expectedException(DelegationNotFound.class, + () -> delegationService.getDelegateProperties(nodeWithoutDelegate, delegatedProp)); + } + + @Test public void hasAspectOnDelegate() + { + assertTrue(delegationService.hasAspectOnDelegate(nodeWithDelegate, delegatedAspect1)); + + expectedException(DelegationNotFound.class, + () -> delegationService.hasAspectOnDelegate(nodeWithDelegate, undelegatedType)); + + expectedException(DelegateNotFound.class, + () -> delegationService.hasAspectOnDelegate(nodeWithoutDelegate, delegatedAspect1)); + } + + @Test public void getDelegations() + { + final Map expectedDelegations = new HashMap<>(); + expectedDelegations.put(delegate, delegateNode); + + assertEquals(expectedDelegations, delegationService.getDelegations(nodeWithDelegate)); + assertEquals(emptyMap(), delegationService.getDelegations(nodeWithoutDelegate)); + } +} diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationUnitTest.java new file mode 100644 index 0000000000..d63e6409d4 --- /dev/null +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationUnitTest.java @@ -0,0 +1,128 @@ +/* + * 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.metadatadelegation; + +import static java.util.Collections.emptySet; +import static org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationException.InvalidDelegation; +import static org.alfresco.module.org_alfresco_module_rm.test.util.ExceptionUtils.expectedException; +import static org.alfresco.module.org_alfresco_module_rm.test.util.FPUtils.asListFrom; +import static org.alfresco.module.org_alfresco_module_rm.test.util.FPUtils.asSet; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.QName; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.List; + +/** + * Unit tests for {@link Delegation}. + * + * @author Neil Mc Erlean + * @since 3.0.a + */ +public class DelegationUnitTest +{ + @Mock DictionaryService mockDictionaryService; + @Mock NodeService mockNodeService; + + private final DelegationAdminServiceImpl metadataDelegationService = new DelegationAdminServiceImpl(); + + private final NodeRef node1 = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "node1"); + private final NodeRef node2 = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "node2"); + private final QName aspect1 = QName.createQName("test", "aspect1"); + private final QName aspect2 = QName.createQName("test", "aspect2"); + private final QName assoc1 = QName.createQName("test", "assoc1"); + + @Before public void setUp() + { + MockitoAnnotations.initMocks(this); + + metadataDelegationService.setNodeService(mockNodeService); + } + + @Test public void nullOrEmptyDelegatesAreForbidden() + { + List invalidDelegations = asListFrom(() -> new Delegation(), + () -> { + Delegation d = new Delegation(); + d.setAssocType(assoc1); + d.setAspects(null); + d.setDictionaryService(mockDictionaryService); + return d; + }, + () -> { + Delegation d = new Delegation(); + d.setAssocType(assoc1); + d.setAspects(emptySet()); + d.setDictionaryService(mockDictionaryService); + return d; + }, + () -> { + Delegation d = new Delegation(); + d.setAssocType(null); + d.setAspects(asSet(aspect1, aspect2)); + d.setDictionaryService(mockDictionaryService); + return d; + }); + + invalidDelegations.stream() + .forEach(d -> expectedException(InvalidDelegation.class, () -> { + d.validateAndRegister(); + return null; + } + )); + } + + @Test(expected=InvalidDelegation.class) + public void delegateMustHaveAssocThatExists() + { + when(mockDictionaryService.getAssociation(assoc1)).thenReturn(null); + when(mockDictionaryService.getAspect(aspect1)).thenReturn(mock(AspectDefinition.class)); + + Delegation d = new Delegation(); + d.setAssocType(assoc1); + d.setAspects(asSet(aspect1)); + d.setDictionaryService(mockDictionaryService); + d.validateAndRegister(); + } + + @Test(expected=InvalidDelegation.class) + public void delegateMustHaveAspectsAllOfWhichExist() + { + when(mockDictionaryService.getAssociation(assoc1)).thenReturn(mock(AssociationDefinition.class)); + when(mockDictionaryService.getAspect(aspect1)).thenReturn(mock(AspectDefinition.class)); + when(mockDictionaryService.getAspect(aspect2)).thenReturn(null); + + Delegation d = new Delegation(); + d.setAssocType(assoc1); + d.setAspects(asSet(aspect1, aspect2)); + d.setDictionaryService(mockDictionaryService); + d.validateAndRegister(); + } +} From 7ec55f4830d50a66f5e356c986c411e54aac0ba6 Mon Sep 17 00:00:00 2001 From: Neil McErlean Date: Tue, 8 Sep 2015 14:29:30 +0000 Subject: [PATCH 04/12] Massive renaming. Delegate/Delegation becomes Referrer, Referent and things *do* make a little more sense. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/BRANCHES/classified_renditions@111633 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../metadata-delegation-context.xml | 123 ----------- .../metadata-referral-context.xml | 123 +++++++++++ .../org_alfresco_module_rm/module-context.xml | 4 +- .../DelegationAdminService.java | 92 --------- .../DelegationAdminServiceImpl.java | 167 --------------- .../DelegationException.java | 107 ---------- .../DelegationRegistry.java | 85 -------- .../metadatadelegation/DelegationService.java | 96 --------- .../MetadataReferral.java} | 44 ++-- .../ReferralAdminService.java | 103 +++++++++ .../ReferralAdminServiceImpl.java | 179 ++++++++++++++++ .../referredmetadata/ReferralRegistry.java | 91 ++++++++ .../ReferredMetadataException.java | 109 ++++++++++ .../ReferredMetadataService.java | 102 +++++++++ .../ReferredMetadataServiceImpl.java} | 93 +++++---- .../package-info.java | 29 +-- .../DelegationServiceImplUnitTest.java | 194 ----------------- .../MetadataReferralUnitTest.java} | 80 +++---- .../ReferralAdminServiceImplUnitTest.java} | 64 +++--- .../ReferredMetadataServiceImplUnitTest.java | 195 ++++++++++++++++++ 20 files changed, 1064 insertions(+), 1016 deletions(-) delete mode 100644 rm-server/config/alfresco/module/org_alfresco_module_rm/metadata-delegation-context.xml create mode 100644 rm-server/config/alfresco/module/org_alfresco_module_rm/metadata-referral-context.xml delete mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationAdminService.java delete mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationAdminServiceImpl.java delete mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationException.java delete mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationRegistry.java delete mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationService.java rename rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/{metadatadelegation/Delegation.java => referredmetadata/MetadataReferral.java} (63%) create mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralAdminService.java create mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralAdminServiceImpl.java create mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralRegistry.java create mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferredMetadataException.java create mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferredMetadataService.java rename rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/{metadatadelegation/DelegationServiceImpl.java => referredmetadata/ReferredMetadataServiceImpl.java} (51%) rename rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/{metadatadelegation => referredmetadata}/package-info.java (53%) delete mode 100644 rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationServiceImplUnitTest.java rename rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/{metadatadelegation/DelegationUnitTest.java => referredmetadata/MetadataReferralUnitTest.java} (67%) rename rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/{metadatadelegation/DelegationAdminServiceImplUnitTest.java => referredmetadata/ReferralAdminServiceImplUnitTest.java} (60%) create mode 100644 rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferredMetadataServiceImplUnitTest.java diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/metadata-delegation-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/metadata-delegation-context.xml deleted file mode 100644 index 106cf0cb90..0000000000 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/metadata-delegation-context.xml +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - - - - - - - org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationAdminService - - - - - - - - - - - - - - - - - - - - ${server.transaction.mode.default} - - - - - - - - org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationAdminService.attachDelegate=ACL_ALLOW - org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationAdminService.detachDelegate=ACL_ALLOW - org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationAdminService.getDefinedDelegations=ACL_ALLOW - org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationAdminService.getDelegationFor=ACL_ALLOW - org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationAdminService.getDelegationsFrom=ACL_ALLOW - org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationAdminService.*=ACL_DENY - - - - - - - - - - - - - - - - org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationService - - - - - - - - - - - - - - - - - - - - ${server.transaction.mode.default} - - - - - - - - org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationService.hasDelegateForAspect=ACL_ALLOW - org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationService.getDelegateFor=ACL_ALLOW - org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationService.getDelegateProperties=ACL_ALLOW - org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationService.getDelegateProperty=ACL_ALLOW - org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationService.hasAspectOnDelegate=ACL_ALLOW - org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationService.getDelegations=ACL_ALLOW - org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationService.*=ACL_DENY - - - - - - - - - - - - diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/metadata-referral-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/metadata-referral-context.xml new file mode 100644 index 0000000000..f5c7c81edb --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/metadata-referral-context.xml @@ -0,0 +1,123 @@ + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferralAdminService + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + + org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferralAdminService.attachReferrer=ACL_ALLOW + org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferralAdminService.detachReferrer=ACL_ALLOW + org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferralAdminService.getDefinedReferrals=ACL_ALLOW + org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferralAdminService.getReferralFor=ACL_ALLOW + org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferralAdminService.getAttachedReferralsFrom=ACL_ALLOW + org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferralAdminService.*=ACL_DENY + + + + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferredMetadataService + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + + org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferredMetadataService.isReferringMetadata=ACL_ALLOW + org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferredMetadataService.getReferentNode=ACL_ALLOW + org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferredMetadataService.getReferredProperties=ACL_ALLOW + org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferredMetadataService.getReferredProperty=ACL_ALLOW + org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferredMetadataService.hasReferredAspect=ACL_ALLOW + org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferredMetadataService.getAttachedReferrals=ACL_ALLOW + org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferredMetadataService.*=ACL_DENY + + + + + + + + + + + + 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 921a34aaf0..e38c0e6e54 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 @@ -267,8 +267,8 @@ - - + + diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationAdminService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationAdminService.java deleted file mode 100644 index 0a49ce4d2c..0000000000 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationAdminService.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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.metadatadelegation; - -import static org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationException.ChainedDelegationUnsupported; - -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.namespace.QName; - -import java.util.Set; - -/** - * A service to manage the delegation of aspect metadata. - * Using this service a node can be {@link #attachDelegate linked} to a delegate node for a configured set of aspects. - * (Note that this delegate node must already exist within the database.) - * Then any read request for relevant metadata such as hasAspect or getProperties can be delegated to the - * linked node. - *

- * For a link to be made, there must be a {@link #getDefinedDelegations() defined Delegation} already in the system. - * This means that a peer-association type will have to have been declared and that a spring bean will have to have - * defined which aspects are to be handled by this {@code Delegation}. - * - * @author Neil Mc Erlean - * @since 3.0.a - */ -public interface DelegationAdminService -{ - /** - * Creates a link between two nodes such that the first {@code nodeRef} can 'inherit' or reuse some aspect - * metadata from another node - the {@code delegateNodeRef}. - *

- * Note that links can currently only extend between two pairs of nodes and cannot be chained. - * - * @param nodeRef the node which is to inherit additional metadata. - * @param delegateNodeRef the node which will provide the additional metadata. - * @param assocType the type of the peer association which will link the two nodes. - * @return a {@link Delegation} object which defines the link type. - * @throws ChainedDelegationUnsupported if an attempt is made to attach nodes such that a chain would be made. - */ - Delegation attachDelegate(NodeRef nodeRef, NodeRef delegateNodeRef, QName assocType); - - /** - * Removes an existing metadata delegation link between two nodes. - * - * @param nodeRef the node which has been linked to a delegate. - * @param assocType the type of the peer assocation forming the link. - * @return the removed {@link Delegation}. - */ - Delegation detachDelegate(NodeRef nodeRef, QName assocType); - - /** - * Gets the set of defined {@link Delegation}s. - * - * @return the set of defined Delegations. - */ - Set getDefinedDelegations(); - - /** - * Gets the {@link Delegation} which contains the specified {@code aspectName} if there is one. - * Note that only one {@link Delegation} may declare that it handles any particular aspect. - * - * @param aspectName the name of the aspect whose {@link Delegation} is sought. - * @return the {@link Delegation} which handles the specified aspect, if there is one. - */ - Delegation getDelegationFor(QName aspectName); - - /** - * Gets the set of {@link Delegation}s which are in effect from the specified {@code nodeRef}. - * From these, you can retrieve the types of peer associations which are linked to the specified - * {@code nodeRef} as well as the aspect types that are handled. - * - * @param nodeRef the NodeRef whose delegations are sought. - * @return the set of {@link Delegation}s from the specified nodeRef. - */ - Set getDelegationsFrom(NodeRef nodeRef); -} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationAdminServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationAdminServiceImpl.java deleted file mode 100644 index 0a2463ba05..0000000000 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationAdminServiceImpl.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * 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.metadatadelegation; - -import static org.alfresco.util.collections.CollectionUtils.transform; - -import org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationException.ChainedDelegationUnsupported; -import org.alfresco.service.cmr.repository.AssociationRef; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.namespace.QName; -import org.alfresco.util.collections.Function; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * @author Neil Mc Erlean - * @since 3.0.a - */ -public class DelegationAdminServiceImpl implements DelegationAdminService -{ - private DelegationRegistry registry; - private NodeService nodeService; - - public void setNodeService(NodeService service) - { - this.nodeService = service; - } - - public void setDelegationRegistry(DelegationRegistry registry) - { - this.registry = registry; - } - - @Override public Delegation attachDelegate(NodeRef nodeRef, NodeRef delegateNodeRef, QName assocType) - { - final Delegation delegation = getDelegationForAssociation(assocType); - - // Prevent the creation of chains of delegation from node A to B to C. - - // If any nodes are already delegating to nodeRef for the specified assoc, then we can't chain. - final List existingDelegatorAssocs = nodeService.getSourceAssocs(nodeRef, assocType); - if ( !existingDelegatorAssocs.isEmpty()) - { - final List existingDelegators = transform(existingDelegatorAssocs, - new Function() - { - @Override public NodeRef apply(AssociationRef assocRef) - { - return assocRef.getSourceRef(); - } - }); - throw new ChainedDelegationUnsupported("Cannot attach delegate", existingDelegators); - } - - // Likewise if this delegate node is already itself delegating elsewhere, we cannot chain. - final List existingDelegateAssocs = nodeService.getTargetAssocs(delegateNodeRef, assocType); - if ( !existingDelegateAssocs.isEmpty()) - { - // If it's not empty, it should only have one value in it, but just in case... - final List existingDelegates = transform(existingDelegateAssocs, - new Function() - { - @Override public NodeRef apply(AssociationRef assocRef) - { - return assocRef.getTargetRef(); - } - }); - throw new ChainedDelegationUnsupported("Cannot attach delegate", existingDelegates); - } - - // OK. We're good to go. We're not making a chain here. - nodeService.createAssociation(nodeRef, delegateNodeRef, assocType); - - return delegation; - } - - private Delegation getDelegationForAssociation(QName assocType) - { - final Delegation delegation = registry.getDelegateForAssociation(assocType); - - if (delegation == null) - { - throw new IllegalArgumentException("No " + Delegation.class.getSimpleName() + - " configured for assocType " + assocType); - } - return delegation; - } - - @Override public Delegation detachDelegate(NodeRef nodeRef, QName assocType) - { - // Is the association there? - final List assocs = nodeService.getTargetAssocs(nodeRef, assocType); - - if (assocs == null || assocs.isEmpty()) - { - return null; - } - else - { - Delegation result = getDelegationForAssociation(assocType); - - // There should only be one such association... but we'll remove them all just in case - for (AssociationRef assocRef : assocs) - { - nodeService.removeAssociation(nodeRef, assocRef.getTargetRef(), assocType); - } - - return result; - } - } - - @Override public Delegation getDelegationFor(QName aspectName) - { - Delegation delegation = null; - - for (Delegation d : getDefinedDelegations()) - { - if (d.getAspects().contains(aspectName)) - { - delegation = d; - break; - } - } - return delegation; - } - - @Override public Set getDelegationsFrom(NodeRef nodeRef) - { - final Set allDelegations = getDefinedDelegations(); - - final Set result = new HashSet<>(); - for (Delegation d : allDelegations) - { - final QName assocType = d.getAssocType(); - if ( !nodeService.getTargetAssocs(nodeRef, assocType).isEmpty()) - { - result.add(d); - } - } - - return result; - } - - @Override public Set getDefinedDelegations() - { - return registry.getDelegations(); - } -} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationException.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationException.java deleted file mode 100644 index ea978abf7b..0000000000 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationException.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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.metadatadelegation; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.service.cmr.repository.NodeRef; - -import java.util.List; - -/** - * Generic class for any runtime exceptions related to metadata delegates. - * - * @author Neil Mc Erlean - * @since 3.0.a - */ -public class DelegationException extends AlfrescoRuntimeException -{ - public DelegationException(String msgId) { super(msgId); } - public DelegationException(String msgId, Throwable cause) { super(msgId, cause); } - - public static class InvalidDelegation extends DelegationException - { - public InvalidDelegation(String msgId) - { - super(msgId); - } - } - - /** A Metadata Delegation already exists. */ - public static class DelegationAlreadyExists extends DelegationException - { - private final Delegation delegation; - - public DelegationAlreadyExists(String msgId, Delegation delegation) - { - super(msgId); - this.delegation = delegation; - } - } - - /** - * A {@link Delegation} has not been found. - * Remember that a Delegation is the definition of a type of link. - */ - public static class DelegationNotFound extends DelegationException - { - public DelegationNotFound(String msgId) - { - super(msgId); - } - } - - /** - * A Delegate has not been found. - * Remember that a Delegate is an instance of a link between two nodes. - */ - public static class DelegateNotFound extends DelegationException - { - public DelegateNotFound(String msgId) - { - super(msgId); - } - } - - /** - * Exception to report that we currently do not support chained delegation. - */ - public static class ChainedDelegationUnsupported extends DelegationException - { - private final List nodesAlreadyDelegating; - - public ChainedDelegationUnsupported(String msgId, List nodesAlreadyDelegating) - { - super(msgId); - this.nodesAlreadyDelegating = nodesAlreadyDelegating; - } - - public List getNodesAlreadyDelegating() - { - return this.nodesAlreadyDelegating; - } - - @Override public String toString() - { - StringBuilder msg = new StringBuilder(); - msg.append(this.getClass().getSimpleName()).append(" Already delegating from: ") - .append(nodesAlreadyDelegating.toString()); - return msg.toString(); - } - } -} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationRegistry.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationRegistry.java deleted file mode 100644 index 54133ee40b..0000000000 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationRegistry.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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.metadatadelegation; - -import org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationException.DelegationAlreadyExists; -import org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationException.InvalidDelegation; -import org.alfresco.service.namespace.QName; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -/** - * This is a registry of {@link Delegation delegations} which have been defined in the system. - * - * @author Neil Mc Erlean - * @since 3.0.a - */ -public class DelegationRegistry -{ - private final Set delegations = new HashSet<>(); - - public void register(Delegation delegation) - { - // Various validation steps to do here to ensure we get consistent, sensible Delegations registered. - if (delegations.contains(delegation)) - { - throw new DelegationAlreadyExists("Cannot register duplicate delegation", delegation); - } - for (Delegation existingDelegation : delegations) - { - if (existingDelegation.getAssocType().equals(delegation.getAssocType())) - { - throw new InvalidDelegation("Cannot register two delegations with the same assocType. " + - "Existing: " + existingDelegation + - " New: " + delegation); - } - // Yes this is a for loop inside a for loop but we're assuming these sets will not be large. - for (QName existingAspect : existingDelegation.getAspects()) - { - if (delegation.getAspects().contains(existingAspect)) - { - throw new InvalidDelegation("Cannot register two delegations with the same aspect. " + - "Existing: " + existingDelegation + - " New: " + delegation); - } - } - } - - this.delegations.add(delegation); - } - - public Set getDelegations() - { - return Collections.unmodifiableSet(delegations); - } - - public Delegation getDelegateForAssociation(QName assocType) - { - for (Delegation d : delegations) - { - if (d.getAssocType().equals(assocType)) - { - return d; - } - } - return null; - } -} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationService.java deleted file mode 100644 index b4048761c0..0000000000 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationService.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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.metadatadelegation; - -import static org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationException.DelegationNotFound; - -import org.alfresco.service.cmr.repository.InvalidNodeRefException; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.namespace.QName; - -import java.io.Serializable; -import java.util.Map; - -/** - * This service provides read-only access to delegated metadata. - * TODO complete. - * - * @author Neil Mc Erlean - * @since 3.0.a - */ -public interface DelegationService -{ - /** - * Checks if the specified nodeRef has an attached {@link Delegation} for the specified aspect. - * - * @param nodeRef the nodeRef which may or may not have a delegate node for the specified aspect. - * @param aspectName the name of the aspect for which the node may or may not have delegation. - * @return whether the node is delegating metadata reads for the specified aspect. - * @throws InvalidNodeRefException if the supplied nodeRef does not exist. - * @throws DelegationNotFound if no delegation for the specified aspect has been attached. - */ - boolean hasDelegateForAspect(NodeRef nodeRef, QName aspectName); - - /** - * Gets the delegate node for the specified aspect, if there is one. - * - * @param nodeRef the node with the delegate. - * @param aspectName the aspect name. - * @return the nodeRef of the delegate if there is one, else {@code null}. - * @throws DelegationNotFound if no delegation for the specified aspect has been attached. - */ - NodeRef getDelegateFor(NodeRef nodeRef, QName aspectName); - - /** - * Gets all the property values from the delegate node for the specified aspect. - * - * @param nodeRef the node with the delegate. - * @param aspectName the aspect name which holds the properties we want. - * @return the property values as obtained from the delegate node. - */ - Map getDelegateProperties(NodeRef nodeRef, QName aspectName); - - /** - * Gets the specified property value from the delegate node. - * - * @param nodeRef the node with the delegate. - * @param propertyName the property name which we want. - * @return the property value as obtained from the delegate node. - */ - Serializable getDelegateProperty(NodeRef nodeRef, QName propertyName); - - /** - * Determines if a given aspect is present on a node's delegates. - * - * @param nodeRef the node for which a delegate is sought. - * @param aspectName the aspect which is to be checked. - * @return Returns true if the aspect has been applied to one of the given node's delegates, - * otherwise false - */ - boolean hasAspectOnDelegate(NodeRef nodeRef, QName aspectName); - - /** - * Gets all {@link Delegation delegations} currently attached to the specified node. - * - * @param nodeRef the node whose delegations are sought. - * @return Returns a map of all {@link Delegation delegations} by NodeRef for the specified nodeRef. - */ - Map getDelegations(NodeRef nodeRef); -} - diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/Delegation.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/MetadataReferral.java similarity index 63% rename from rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/Delegation.java rename to rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/MetadataReferral.java index 05a2e3caef..2b739e6a23 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/Delegation.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/MetadataReferral.java @@ -16,9 +16,9 @@ * 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.metadatadelegation; +package org.alfresco.module.org_alfresco_module_rm.referredmetadata; -import org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationException.InvalidDelegation; +import org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferredMetadataException.InvalidMetadataReferral; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.namespace.QName; @@ -27,27 +27,29 @@ import java.util.Objects; import java.util.Set; /** - * A Delegation is a definition of a {@link #aspects set of aspects} whose metadata are to be delegated. - * Using a Delegation, you can attach a delegate node to any node and {@code hasAspect} and - * {@code getPropert[y|ies]} calls can be delegated to the delegate node. + * A {@link MetadataReferral} is a definition of a {@link #aspects set of metadata} which are, in effect, shared + * between multiple nodes. + * Using a {@link MetadataReferral}, you can link two NodeRefs such that {@code hasAspect} and + * {@code getPropert[y|ies]} calls on one node can can be delegated to the other. In this way a defined set of + * metadata on one node can be made available for read access via another node. *

* The connection between the nodes is made with a specified {@link #assocType peer association}. *

- * Note that a Delegation is not an instance of a link between two nodes, but the definition of such a link. + * Note that a {@link MetadataReferral} is not an instance of a link between two nodes, but the definition of such a link. * * @author Neil Mc Erlean - * @since 3.0.a + * @since 2.4.a */ -public class Delegation +public class MetadataReferral { private DictionaryService dictionaryService; - private DelegationRegistry delegationRegistry; + private ReferralRegistry referralRegistry; private Set aspects; private QName assocType; - public Delegation() + public MetadataReferral() { - // Intentionally empty + // Intentionally empty. } public void setDictionaryService(DictionaryService service) @@ -55,9 +57,9 @@ public class Delegation this.dictionaryService = service; } - public void setDelegationRegistry(DelegationRegistry registry) + public void setReferralRegistry(ReferralRegistry registry) { - this.delegationRegistry = registry; + this.referralRegistry = registry; } public void setAssocType(QName assocType) @@ -74,25 +76,25 @@ public class Delegation { if (this.assocType == null) { - throw new InvalidDelegation("Illegal null assocType"); + throw new InvalidMetadataReferral("Illegal null assocType"); } if (aspects == null || aspects.isEmpty()) { - throw new InvalidDelegation("Illegal null or empty aspects set"); + throw new InvalidMetadataReferral("Illegal null or empty aspects set"); } if (dictionaryService.getAssociation(assocType) == null) { - throw new InvalidDelegation("Association not found: " + assocType); + throw new InvalidMetadataReferral("Association not found: " + assocType); } for (QName aspect : aspects) { if (dictionaryService.getAspect(aspect) == null) { - throw new InvalidDelegation("Aspect not found: " + aspect); + throw new InvalidMetadataReferral("Aspect not found: " + aspect); } } - this.delegationRegistry.register(this); + this.referralRegistry.register(this); } /** Gets the type of the peer association linking the node to its delegate. */ @@ -101,7 +103,7 @@ public class Delegation return assocType; } - /** Gets the set of aspects which are being delegated. */ + /** Gets the set of aspects which are being referred. */ public Set getAspects() { return Collections.unmodifiableSet(aspects); @@ -115,9 +117,9 @@ public class Delegation @Override public boolean equals(Object other) { boolean result = false; - if (other instanceof Delegation) + if (other instanceof MetadataReferral) { - Delegation that = (Delegation)other; + MetadataReferral that = (MetadataReferral)other; result = this.aspects.equals(that.aspects) && this.assocType.equals(that.assocType); } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralAdminService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralAdminService.java new file mode 100644 index 0000000000..cafad45b0e --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralAdminService.java @@ -0,0 +1,103 @@ +/* + * 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.referredmetadata; + +import static org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferredMetadataException.ChainedMetadataReferralUnsupported; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +import java.util.Set; + +/** + * A service to manage the referral of aspect metadata. + * Using this service a node can be {@link #attachReferrer linked} to a referrer node for a specific set of aspects. + * (Note that this referrer node must already exist within the database.) + * Then any read request for relevant metadata such as hasAspect or getProperties can be delegated to the + * linked node. + *

+ * For a link to be made, there must be a {@link #getDefinedReferrals() defined MetadataReferral} already in the system. + * This means that a peer-association type will have to have been declared and that a spring bean will have to have + * defined which aspects are to be handled by this {@link MetadataReferral}. + * + * @author Neil Mc Erlean + * @since 2.4.a + */ +public interface ReferralAdminService +{ + /** + * Creates a link between two nodes such that the first {@code referrer} can 'inherit' or reuse some aspect + * metadata from another node - the {@code referrer}. + *

+ * Note that links can currently only extend between two pairs of nodes and cannot be chained. + * + * @param referrer the node which is to inherit additional metadata. + * @param referent the node which will provide the additional metadata. + * @param assocType the type of the peer association which will link the two nodes. + * @return a {@link MetadataReferral} object which defines the link type. + * @throws ChainedMetadataReferralUnsupported if an attempt is made to attach nodes such that a chain would be made. + */ + // FIXME Remove assocType entirely from the API + MetadataReferral attachReferrer(NodeRef referrer, NodeRef referent, QName assocType); + + /** + * Removes an existing metadata link between two nodes. + * + * @param referrer the node which has been linked to a metadata source. + * @param assocType the type of the peer association forming the link. + * @return the removed {@link MetadataReferral}. + */ + MetadataReferral detachReferrer(NodeRef referrer, QName assocType); + + /** + * Gets the set of defined {@link MetadataReferral}s. + * + * @return the set of defined {@link MetadataReferral}. + */ + Set getDefinedReferrals(); + + /** + * Gets the {@link MetadataReferral} which contains the specified {@code aspectName} if there is one. + * Note that only one {@link MetadataReferral} may declare that it handles any particular aspect. + * + * @param aspectName the name of the aspect whose {@link MetadataReferral} is sought. + * @return the {@link MetadataReferral} which handles the specified aspect, if there is one. + */ + MetadataReferral getReferralFor(QName aspectName); + + /** + * Gets the set of {@link MetadataReferral}s which are currently applied from the specified {@code referrer}. + * From these, the types of peer associations which are linked to the specified + * {@code referrer} as well as the aspect types that are handled can be retrieved. + * + * @param referrer the NodeRef whose {@link MetadataReferral}s are sought. + * @return the set of {@link MetadataReferral}s from the specified referrer. + */ + Set getAttachedReferralsFrom(NodeRef referrer); + + /** + * Gets the {@link MetadataReferral} from the specified {@code referrer} for the specified {@code aspectName}, + * if there is one. + * + * @param referrer the node whose {@link MetadataReferral} is sought. + * @param aspectName the aspect name for which a {@link MetadataReferral} is sought. + * @return the {@link MetadataReferral} which is attached to the specified node if there is one, else {@code null}. + */ + MetadataReferral getAttachedReferralFrom(NodeRef referrer, QName aspectName); +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralAdminServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralAdminServiceImpl.java new file mode 100644 index 0000000000..117261b990 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralAdminServiceImpl.java @@ -0,0 +1,179 @@ +/* + * 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.referredmetadata; + +import static org.alfresco.util.collections.CollectionUtils.transform; + +import org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferredMetadataException.ChainedMetadataReferralUnsupported; +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.collections.Function; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @author Neil Mc Erlean + * @since 2.4.a + */ +public class ReferralAdminServiceImpl implements ReferralAdminService +{ + private ReferralRegistry registry; + private NodeService nodeService; + + public void setNodeService(NodeService service) + { + this.nodeService = service; + } + + public void setReferralRegistry(ReferralRegistry registry) + { + this.registry = registry; + } + + @Override public MetadataReferral attachReferrer(NodeRef referrer, NodeRef referent, QName assocType) + { + final MetadataReferral metadataReferral = getReferralForAssociation(assocType); + + // Prevent the creation of chains of metadata linking from node A to B to C. + + // If any nodes are already linked to referrer for the specified assoc, then we can't chain. + final List existingReferrerAssocs = nodeService.getSourceAssocs(referrer, assocType); + if ( !existingReferrerAssocs.isEmpty()) + { + final List existingReferrers = transform(existingReferrerAssocs, + new Function() + { + @Override public NodeRef apply(AssociationRef assocRef) + { + return assocRef.getSourceRef(); + } + }); + throw new ChainedMetadataReferralUnsupported("Cannot attach referrer", existingReferrers); + } + + // Likewise if this referent node is already itself linked elsewhere, we cannot chain. + final List existingReferentAssocs = nodeService.getTargetAssocs(referent, assocType); + if ( !existingReferentAssocs.isEmpty()) + { + // If it's not empty, it should only have one value in it, but just in case... + final List existingReferents = transform(existingReferentAssocs, + new Function() + { + @Override public NodeRef apply(AssociationRef assocRef) + { + return assocRef.getTargetRef(); + } + }); + throw new ChainedMetadataReferralUnsupported("Cannot attach referent", existingReferents); + } + + // OK. We're good to go. We're not making a chain here. + nodeService.createAssociation(referrer, referent, assocType); + + return metadataReferral; + } + + /** Gets the {@link MetadataReferral} which uses the specified {@code assocType}. */ + private MetadataReferral getReferralForAssociation(QName assocType) + { + final MetadataReferral metadataReferral = registry.getReferralForAssociation(assocType); + + if (metadataReferral == null) + { + throw new IllegalArgumentException("No " + MetadataReferral.class.getSimpleName() + + " configured for assocType " + assocType); + } + return metadataReferral; + } + + @Override public MetadataReferral detachReferrer(NodeRef referrer, QName assocType) + { + // Is the association there? + final List assocs = nodeService.getTargetAssocs(referrer, assocType); + + if (assocs == null || assocs.isEmpty()) + { + return null; + } + else + { + MetadataReferral result = getReferralForAssociation(assocType); + + // There should only be one such association... but we'll remove them all just in case + for (AssociationRef assocRef : assocs) + { + nodeService.removeAssociation(referrer, assocRef.getTargetRef(), assocType); + } + + return result; + } + } + + @Override public MetadataReferral getReferralFor(QName aspectName) + { + MetadataReferral metadataReferral = null; + + for (MetadataReferral d : getDefinedReferrals()) + { + if (d.getAspects().contains(aspectName)) + { + metadataReferral = d; + break; + } + } + return metadataReferral; + } + + @Override public Set getAttachedReferralsFrom(NodeRef referrer) + { + final Set allMetadataReferrals = getDefinedReferrals(); + + final Set result = new HashSet<>(); + for (MetadataReferral d : allMetadataReferrals) + { + final QName assocType = d.getAssocType(); + if ( !nodeService.getTargetAssocs(referrer, assocType).isEmpty()) + { + result.add(d); + } + } + + return result; + } + + @Override public MetadataReferral getAttachedReferralFrom(NodeRef referrer, QName aspectName) + { + final Set allMetadataReferrals = getAttachedReferralsFrom(referrer); + + for (MetadataReferral d : allMetadataReferrals) + { + if (d.getAspects().contains(aspectName)) return d; + } + return null; + } + + @Override public Set getDefinedReferrals() + { + return registry.getMetadataReferrals(); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralRegistry.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralRegistry.java new file mode 100644 index 0000000000..2dacba8d45 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralRegistry.java @@ -0,0 +1,91 @@ +/* + * 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.referredmetadata; + +import org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferredMetadataException.MetadataReferralAlreadyExists; +import org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferredMetadataException.InvalidMetadataReferral; +import org.alfresco.service.namespace.QName; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * This is a registry of {@link MetadataReferral}s which have been defined in the system. + * + * @author Neil Mc Erlean + * @since 2.4.a + */ +public class ReferralRegistry +{ + private final Set metadataReferrals = new HashSet<>(); + + public void register(MetadataReferral metadataReferral) + { + // Various validation steps to do here to ensure we get consistent, sensible referrals registered. + if (metadataReferrals.contains(metadataReferral)) + { + throw new MetadataReferralAlreadyExists("Cannot register duplicate referral", metadataReferral); + } + for (MetadataReferral existingMetadataReferral : metadataReferrals) + { + if (existingMetadataReferral.getAssocType().equals(metadataReferral.getAssocType())) + { + throw new InvalidMetadataReferral("Cannot register two referrals with the same assocType. " + + "Existing: " + existingMetadataReferral + + " New: " + metadataReferral); + } + // Yes this is a for loop inside a for loop but we're assuming these sets will not be large. + for (QName existingAspect : existingMetadataReferral.getAspects()) + { + if (metadataReferral.getAspects().contains(existingAspect)) + { + throw new InvalidMetadataReferral("Cannot register two referrals with the same aspect. " + + "Existing: " + existingMetadataReferral + + " New: " + metadataReferral); + } + } + } + + this.metadataReferrals.add(metadataReferral); + } + + public Set getMetadataReferrals() + { + return Collections.unmodifiableSet(metadataReferrals); + } + + /** + * Gets the {@link MetadataReferral} which is defined to use the specified {@code assocType}. + * + * @param assocType the peer association type whose {@link MetadataReferral} is sought. + * @return the {@link MetadataReferral} defined to use the specified {@code assocType} if there is one, else {@code null}. + */ + public MetadataReferral getReferralForAssociation(QName assocType) + { + for (MetadataReferral d : metadataReferrals) + { + if (d.getAssocType().equals(assocType)) + { + return d; + } + } + return null; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferredMetadataException.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferredMetadataException.java new file mode 100644 index 0000000000..83cbb83ed1 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferredMetadataException.java @@ -0,0 +1,109 @@ +/* + * 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.referredmetadata; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.service.cmr.repository.NodeRef; + +import java.util.List; + +/** + * Generic class for any runtime exceptions related to metadata referrals. + * + * @author Neil Mc Erlean + * @since 2.4.a + */ +public class ReferredMetadataException extends AlfrescoRuntimeException +{ + public ReferredMetadataException(String msgId) { super(msgId); } + public ReferredMetadataException(String msgId, Throwable cause) { super(msgId, cause); } + + /** This exception may be thrown when a {@link MetadataReferral} was incorrectly initialised. */ + public static class InvalidMetadataReferral extends ReferredMetadataException + { + public InvalidMetadataReferral(String msgId) + { + super(msgId); + } + } + + /** This exception may be thrown when a {@link MetadataReferral} already exists. */ + public static class MetadataReferralAlreadyExists extends ReferredMetadataException + { + private final MetadataReferral metadataReferral; + + public MetadataReferralAlreadyExists(String msgId, MetadataReferral metadataReferral) + { + super(msgId); + this.metadataReferral = metadataReferral; + } + } + + /** A {@link MetadataReferral} has not been found. */ + public static class MetadataReferralNotFound extends ReferredMetadataException + { + public MetadataReferralNotFound(String msgId) + { + super(msgId); + } + } + + /** A referent Node has not been found. */ + public static class ReferentNodeNotFound extends ReferredMetadataException + { + public ReferentNodeNotFound(String msgId) + { + super(msgId); + } + } + + /** Exception to report that chains of metadata referral are not currently supported. */ + public static class ChainedMetadataReferralUnsupported extends ReferredMetadataException + { + private final List existingReferrers; + + public ChainedMetadataReferralUnsupported(String msgId, List existingReferrers) + { + super(msgId); + this.existingReferrers = existingReferrers; + } + + public List getExistingReferrers() + { + return this.existingReferrers; + } + + @Override public String toString() + { + StringBuilder msg = new StringBuilder(); + msg.append(this.getClass().getSimpleName()).append(" Already referring from: ") + .append(existingReferrers.toString()); + return msg.toString(); + } + } + + /** Exception to report that metadata referral is not supported for metadata defined on content types. */ + public static class TypeMetadataReferralUnsupported extends ReferredMetadataException + { + public TypeMetadataReferralUnsupported(String msgId) + { + super(msgId); + } + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferredMetadataService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferredMetadataService.java new file mode 100644 index 0000000000..8e18c2bcf7 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferredMetadataService.java @@ -0,0 +1,102 @@ +/* + * 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.referredmetadata; + +import static org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferredMetadataException.MetadataReferralNotFound; +import static org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferredMetadataException.TypeMetadataReferralUnsupported; + +import org.alfresco.service.cmr.repository.InvalidNodeRefException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +import java.io.Serializable; +import java.util.Map; + +/** + * This service provides read-only access to linked metadata. + * TODO complete! Include definition of referent etc or link to package.html + * + * @author Neil Mc Erlean + * @since 2.4.a + */ +public interface ReferredMetadataService +{ + /** + * Checks if the specified referrer has an attached {@link MetadataReferral} for the specified aspect. + * + * @param potentialReferrer the referrer which may or may not be linked to a referent node. + * @param aspectName the name of the aspect. + * @return whether the node is linked to a referent node for the specified aspect. + * @throws InvalidNodeRefException if the supplied referrer does not exist. + * @throws MetadataReferralNotFound if no {@link MetadataReferral} is defined for the specified aspect. + */ + boolean isReferringMetadata(NodeRef potentialReferrer, QName aspectName); + + /** + * Gets the referent node for the specified aspect, if there is one. + * + * @param referrer the node whose referent is sought. + * @param aspectName the aspect name. + * @return the referent of the provided referrer if there is one, else {@code null}. + * @throws InvalidNodeRefException if the supplied referrer does not exist. + * @throws MetadataReferralNotFound if no {@link MetadataReferral} is defined for the specified aspect. + */ + NodeRef getReferentNode(NodeRef referrer, QName aspectName); + + /** + * Gets all the property values from the referent node for the specified aspect. + * + * @param referrer the referring node. + * @param aspectName the aspect name which holds the properties we want. + * @return the property values as obtained from the referent node. + */ + Map getReferredProperties(NodeRef referrer, QName aspectName); + + /** + * Gets the specified property value from the referent node. + * + * @param referrer the referring node. + * @param propertyName the property name whose value is sought. + * @return the property value as obtained from the referent node. + * @throws IllegalArgumentException if the specified property is not defined. + * @throws TypeMetadataReferralUnsupported if the specified property is not defined on an aspect. + */ + Serializable getReferredProperty(NodeRef referrer, QName propertyName); + + /** + * Determines if the specified aspect is present on a node's referent. + * + * @param referrer the referring node. + * @param aspectName the aspect which is to be checked on the referent node. + * @return Returns true if the aspect has been applied to the referent node, + * otherwise false + */ + boolean hasReferredAspect(NodeRef referrer, QName aspectName); + + /** + * Gets all {@link MetadataReferral referrals} currently attached to the specified node. + * + * @param potentialReferrer the node whose attached {@link MetadataReferral referrals} are sought. + * @return Returns a map of all attached {@link MetadataReferral referrals} for the specified nodeRef. + * The map has the form {@code (key, value) = (MetadataReferral, referent Node for that Referral)} + * The map may be empty but will not be {@code null}. + */ + Map getAttachedReferrals(NodeRef potentialReferrer); +} + diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferredMetadataServiceImpl.java similarity index 51% rename from rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationServiceImpl.java rename to rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferredMetadataServiceImpl.java index 3b7190c7d5..add2c367c2 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferredMetadataServiceImpl.java @@ -16,10 +16,11 @@ * 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.metadatadelegation; +package org.alfresco.module.org_alfresco_module_rm.referredmetadata; -import static org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationException.DelegateNotFound; -import static org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationException.DelegationNotFound; +import static org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferredMetadataException.MetadataReferralNotFound; +import static org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferredMetadataException.ReferentNodeNotFound; +import static org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferredMetadataException.TypeMetadataReferralUnsupported; import static org.alfresco.util.collections.CollectionUtils.filterKeys; import org.alfresco.service.cmr.dictionary.ClassDefinition; @@ -40,17 +41,17 @@ import java.util.Set; /** * @author Neil Mc Erlean - * @since 3.0.a + * @since 2.4.a */ -public class DelegationServiceImpl implements DelegationService +public class ReferredMetadataServiceImpl implements ReferredMetadataService { - private DelegationAdminService delegationAdminService; - private DictionaryService dictionaryService; - private NodeService nodeService; + private ReferralAdminService referralAdminService; + private DictionaryService dictionaryService; + private NodeService nodeService; - public void setDelegationAdminService(DelegationAdminService service) + public void setReferralAdminService(ReferralAdminService service) { - this.delegationAdminService = service; + this.referralAdminService = service; } public void setDictionaryService(DictionaryService service) @@ -63,54 +64,60 @@ public class DelegationServiceImpl implements DelegationService this.nodeService = service; } - @Override public boolean hasDelegateForAspect(NodeRef nodeRef, QName aspectName) + @Override public boolean isReferringMetadata(NodeRef potentialReferrer, QName aspectName) { - final Delegation delegation = delegationAdminService.getDelegationFor(aspectName); - - if ( !nodeService.exists(nodeRef)) + if ( !nodeService.exists(potentialReferrer)) { - throw new InvalidNodeRefException(nodeRef); + throw new InvalidNodeRefException(potentialReferrer); } - else if (delegation == null) + + final MetadataReferral metadataReferral = referralAdminService.getReferralFor(aspectName); + + if (metadataReferral == null) { - throw new DelegationNotFound("No delegation found for aspect: " + aspectName); + throw new MetadataReferralNotFound("No defined referral found for aspect: " + aspectName); } else { - final List targetAssocs = nodeService.getTargetAssocs(nodeRef, delegation.getAssocType()); + final List targetAssocs = nodeService.getTargetAssocs(potentialReferrer, metadataReferral.getAssocType()); return !targetAssocs.isEmpty(); } } - @Override public NodeRef getDelegateFor(NodeRef nodeRef, QName aspectName) + @Override public NodeRef getReferentNode(NodeRef referrer, QName aspectName) { - final Delegation d = delegationAdminService.getDelegationFor(aspectName); + if ( !nodeService.exists(referrer)) + { + throw new InvalidNodeRefException(referrer); + } + + final MetadataReferral d = referralAdminService.getReferralFor(aspectName); if (d == null) { - throw new DelegationNotFound("No delegation found for aspect: " + aspectName); + throw new MetadataReferralNotFound("No defined referral found for aspect: " + aspectName); } else { final QName assocType = d.getAssocType(); - final List assocs = nodeService.getTargetAssocs(nodeRef, assocType); + final List assocs = nodeService.getTargetAssocs(referrer, assocType); return assocs.isEmpty() ? null : assocs.get(0).getTargetRef(); } } - @Override public Map getDelegateProperties(NodeRef nodeRef, final QName aspectName) + @Override public Map getReferredProperties(NodeRef referrer, final QName aspectName) { - final NodeRef delegateNode = getDelegateFor(nodeRef, aspectName); + final NodeRef referentNode = getReferentNode(referrer, aspectName); - if (delegateNode == null) + if (referentNode == null) { - throw new DelegateNotFound("No delegate node found for " + nodeRef + " " + aspectName); + throw new ReferentNodeNotFound("No referent node found for " + referrer + " " + aspectName); } else { - Map allProps = nodeService.getProperties(delegateNode); - Map aspectProps = filterKeys(allProps, + final Map allProps = nodeService.getProperties(referentNode); + final Map aspectProps = filterKeys(allProps, new Function() { @Override public Boolean apply(QName propName) @@ -125,7 +132,7 @@ public class DelegationServiceImpl implements DelegationService } } - @Override public Serializable getDelegateProperty(NodeRef nodeRef, QName propertyName) + @Override public Serializable getReferredProperty(NodeRef referrer, QName propertyName) { final PropertyDefinition propDefn = dictionaryService.getProperty(propertyName); @@ -141,38 +148,38 @@ public class DelegationServiceImpl implements DelegationService msg.append("Property '").append(propertyName).append("' is not defined on an aspect: ") .append(aspectDefn.getName()); - throw new IllegalArgumentException(msg.toString()); + throw new TypeMetadataReferralUnsupported(msg.toString()); } - Map allPropValues = getDelegateProperties(nodeRef, aspectDefn.getName()); + final Map allPropValues = getReferredProperties(referrer, aspectDefn.getName()); return allPropValues.get(propertyName); } - @Override public boolean hasAspectOnDelegate(NodeRef nodeRef, QName aspectName) + @Override public boolean hasReferredAspect(NodeRef referrer, QName aspectName) { - final NodeRef delegateNode = getDelegateFor(nodeRef, aspectName); + final NodeRef referentNode = getReferentNode(referrer, aspectName); - if (delegateNode == null) + if (referentNode == null) { - throw new DelegateNotFound("No delegate node found for " + nodeRef + " " + aspectName); + throw new ReferentNodeNotFound("No referent node found for " + referrer + " " + aspectName); } else { - return nodeService.hasAspect(delegateNode, aspectName); + return nodeService.hasAspect(referentNode, aspectName); } } - @Override public Map getDelegations(NodeRef nodeRef) + @Override public Map getAttachedReferrals(NodeRef potentialReferrer) { - Set delegations = delegationAdminService.getDelegationsFrom(nodeRef); + Set metadataReferrals = referralAdminService.getAttachedReferralsFrom(potentialReferrer); - Map result = new HashMap<>(); - for (Delegation d : delegations) + Map result = new HashMap<>(); + for (MetadataReferral mr : metadataReferrals) { - // We need only use the first aspect to get the Delegation object - if (!d.getAspects().isEmpty()) + // We need only use the first aspect to get the MetadataReferral object + if (!mr.getAspects().isEmpty()) { - result.put(d, getDelegateFor(nodeRef, d.getAspects().iterator().next())); + result.put(mr, getReferentNode(potentialReferrer, mr.getAspects().iterator().next())); } } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/package-info.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/package-info.java similarity index 53% rename from rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/package-info.java rename to rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/package-info.java index b35ca04e74..7a1a00ae73 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/package-info.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/package-info.java @@ -17,30 +17,31 @@ * along with Alfresco. If not, see . */ /** - * This package contains the types that deliver the Metadata Delegation feature. - * Metadata delegation allows read-only aspect metadata for any given Alfresco node to - * be sourced from another node, the delegate. + * This package contains the types that deliver the Metadata Referral feature. + * Metadata referral allows node metadata to be shared between multiple Alfresco nodes. *

* In this way nodes can 'inherit' some of their metadata from another node which may * have benefits when more than one node is required to share some of the same metadata. *

- * Multiple nodes may share the same delegate node and one node may be linked to multiple - * delegates. + * Only aspect metadata can be shared and it is only shared as read-only data to the other nodes. + * The node which contains the metadata values is the 'referent' node and any nodes which have been + * linked to the referent and share the metadata are known as referrers. *

- * The linking of nodes to their metadata delegates is done with Alfresco peer associations. + * Multiple nodes may share the same referent node and one node may be linked to multiple referrers. + *

+ * The linking of nodes to their metadata referents is done with Alfresco peer associations. * Association types must be declared in an Alfresco content model in the normal way. * Spring configuration is used to assign each association type a set of aspects which will - * be available from the delegate via the association. + * be available from the referent via the association. *

- * See {@link org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationAdminService} - * for details on how to create and destroy delegation links between nodes. + * See {@link org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferralAdminService} + * for details on how to create and destroy metadata links between nodes. *

- * The read-only access to delegated metadat is made available via the - * See {@link org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationService} + * See {@link org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferredMetadataService} * for details on how the data access is performed. *

- * See {@link org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationRegistry} - * for details on what {@link org.alfresco.module.org_alfresco_module_rm.metadatadelegation.Delegation}s + * See {@link org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferralRegistry} + * for details on what {@link org.alfresco.module.org_alfresco_module_rm.referredmetadata.MetadataReferral}s * are defined in the system. */ -package org.alfresco.module.org_alfresco_module_rm.metadatadelegation; \ No newline at end of file +package org.alfresco.module.org_alfresco_module_rm.referredmetadata; \ No newline at end of file diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationServiceImplUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationServiceImplUnitTest.java deleted file mode 100644 index b28fae5de4..0000000000 --- a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationServiceImplUnitTest.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * 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.metadatadelegation; - -import static java.util.Collections.emptyMap; -import static java.util.Arrays.asList; -import static org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationException.DelegateNotFound; -import static org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationException.DelegationNotFound; -import static org.alfresco.module.org_alfresco_module_rm.test.util.ExceptionUtils.expectedException; -import static org.alfresco.module.org_alfresco_module_rm.test.util.FPUtils.asSet; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.when; - -import org.alfresco.service.cmr.dictionary.ClassDefinition; -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.cmr.dictionary.PropertyDefinition; -import org.alfresco.service.cmr.repository.AssociationRef; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.namespace.QName; -import org.junit.Before; -import org.junit.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; - -/** - * Unit tests for {@link DelegationServiceImpl}. - * - * @author Neil Mc Erlean - * @since 3.0.a - */ -public class DelegationServiceImplUnitTest -{ - @InjectMocks private final DelegationServiceImpl delegationService = new DelegationServiceImpl(); - - @Mock DictionaryService mockDictionaryService; - @Mock NodeService mockNodeService; - @Mock DelegationAdminServiceImpl mockDelegationAdminService; - - /** This node has a delegate node. */ - private final NodeRef nodeWithDelegate = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "nodeWithDelegate"); - /** This is the delgate for {@link #nodeWithDelegate}. */ - private final NodeRef delegateNode = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "delegateNode"); - /** This node has no delegate node. */ - private final NodeRef nodeWithoutDelegate = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "nodeWithoutDelegate"); - - /** The type of the peer association that links the delegate to its source. */ - private final QName delegateAssocType = QName.createQName("test", "delegateAssocType"); - /** The instance of the association between {@link #nodeWithDelegate} and {@link #delegateNode}. */ - private final AssociationRef delegateAssocRef = new AssociationRef(nodeWithDelegate, delegateAssocType, delegateNode); - - /** Name of an aspect that has been delegated. */ - private final QName delegatedAspect1 = QName.createQName("test", "delegatedAspect1"); - /** Name of an aspect that has been delegated. */ - private final QName delegatedAspect2 = QName.createQName("test", "delegatedAspect2"); - /** Name of a content class (a type in this case) that has not been delegated. - * N.B. Types can't be delegated currently. */ - private final QName undelegatedType = QName.createQName("test", "undelegatedType"); - - private final QName delegatedProp = QName.createQName("test", "delegatedProp"); - private final Serializable delegatedPropValue = "hello"; - private final QName undelegatedProp = QName.createQName("test", "undelegatedProp"); - - private final Delegation delegate = new Delegation() - {{ - this.setAssocType(delegateAssocType); - this.setAspects(asSet(delegatedAspect1, delegatedAspect2)); - }}; - - @Before public void setUp() - { - MockitoAnnotations.initMocks(this); - - final PropertyDefinition aspectProp = mock(PropertyDefinition.class); - final ClassDefinition aspectDefn = mock(ClassDefinition.class); - when(aspectDefn.getName()).thenReturn(delegatedAspect1); - when(aspectProp.getContainerClass()).thenReturn(aspectDefn); - when(aspectDefn.isAspect()).thenReturn(true); - - final PropertyDefinition typeProp = mock(PropertyDefinition.class); - final ClassDefinition typeDefn = mock(ClassDefinition.class); - when(typeDefn.getName()).thenReturn(undelegatedType); - when(typeProp.getContainerClass()).thenReturn(typeDefn); - when(typeDefn.isAspect()).thenReturn(false); - - when(mockDictionaryService.getProperty(delegatedProp)).thenReturn(aspectProp); - - when(mockDelegationAdminService.getDelegationsFrom(nodeWithDelegate)).thenReturn(asSet(delegate)); - for (QName delegatedAspect : asSet(delegatedAspect1, delegatedAspect2)) - { - when(mockDelegationAdminService.getDelegationFor(delegatedAspect)).thenReturn(delegate); - when(mockNodeService.hasAspect(delegateNode, delegatedAspect)).thenReturn(true); - } - when(mockNodeService.getSourceAssocs(delegateNode, delegateAssocType)).thenReturn(asList(delegateAssocRef)); - when(mockNodeService.getTargetAssocs(nodeWithDelegate, delegateAssocType)).thenReturn(asList(delegateAssocRef)); - when(mockNodeService.exists(any(NodeRef.class))).thenReturn(true); - when(mockNodeService.getProperties(delegateNode)) - .thenReturn(new HashMap() - {{ - this.put(delegatedProp, delegatedPropValue); - }}); - } - - @Test public void hasDelegateForAspect() - { - assertTrue(delegationService.hasDelegateForAspect(nodeWithDelegate, delegatedAspect1)); - expectedException(DelegationNotFound.class, () -> delegationService.hasDelegateForAspect(nodeWithoutDelegate, undelegatedType)); - assertFalse(delegationService.hasDelegateForAspect(nodeWithoutDelegate, delegatedAspect1)); - } - - @Test public void getDelegateFor() - { - assertEquals(delegateNode, delegationService.getDelegateFor(nodeWithDelegate, delegatedAspect1)); - expectedException(DelegationNotFound.class, () -> - { - delegationService.getDelegateFor(nodeWithDelegate, undelegatedType); - return null; - }); - assertNull(delegationService.getDelegateFor(nodeWithoutDelegate, delegatedAspect1)); - } - - @Test public void getDelegateProperties() - { - final Map expectedProps = new HashMap<>(); - expectedProps.put(delegatedProp, delegatedPropValue); - - assertEquals(expectedProps, delegationService.getDelegateProperties(nodeWithDelegate, delegatedAspect1)); - - expectedException(DelegationNotFound.class, - () -> delegationService.getDelegateProperties(nodeWithDelegate, undelegatedType)); - - expectedException(DelegateNotFound.class, - () -> delegationService.getDelegateProperties(nodeWithoutDelegate, delegatedAspect1)); - } - - @Test public void getDelegateProperty() - { - assertEquals(delegatedPropValue, delegationService.getDelegateProperty(nodeWithDelegate, delegatedProp)); - - expectedException(IllegalArgumentException.class, - () -> delegationService.getDelegateProperty(nodeWithDelegate, undelegatedProp)); - - expectedException(DelegationNotFound.class, - () -> delegationService.getDelegateProperties(nodeWithoutDelegate, delegatedProp)); - } - - @Test public void hasAspectOnDelegate() - { - assertTrue(delegationService.hasAspectOnDelegate(nodeWithDelegate, delegatedAspect1)); - - expectedException(DelegationNotFound.class, - () -> delegationService.hasAspectOnDelegate(nodeWithDelegate, undelegatedType)); - - expectedException(DelegateNotFound.class, - () -> delegationService.hasAspectOnDelegate(nodeWithoutDelegate, delegatedAspect1)); - } - - @Test public void getDelegations() - { - final Map expectedDelegations = new HashMap<>(); - expectedDelegations.put(delegate, delegateNode); - - assertEquals(expectedDelegations, delegationService.getDelegations(nodeWithDelegate)); - assertEquals(emptyMap(), delegationService.getDelegations(nodeWithoutDelegate)); - } -} diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/MetadataReferralUnitTest.java similarity index 67% rename from rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationUnitTest.java rename to rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/MetadataReferralUnitTest.java index d63e6409d4..847f64f0d1 100644 --- a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationUnitTest.java +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/MetadataReferralUnitTest.java @@ -16,10 +16,10 @@ * 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.metadatadelegation; +package org.alfresco.module.org_alfresco_module_rm.referredmetadata; import static java.util.Collections.emptySet; -import static org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationException.InvalidDelegation; +import static org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferredMetadataException.InvalidMetadataReferral; import static org.alfresco.module.org_alfresco_module_rm.test.util.ExceptionUtils.expectedException; import static org.alfresco.module.org_alfresco_module_rm.test.util.FPUtils.asListFrom; import static org.alfresco.module.org_alfresco_module_rm.test.util.FPUtils.asSet; @@ -41,17 +41,17 @@ import org.mockito.MockitoAnnotations; import java.util.List; /** - * Unit tests for {@link Delegation}. + * Unit tests for {@link MetadataReferral}. * * @author Neil Mc Erlean * @since 3.0.a */ -public class DelegationUnitTest +public class MetadataReferralUnitTest { @Mock DictionaryService mockDictionaryService; @Mock NodeService mockNodeService; - private final DelegationAdminServiceImpl metadataDelegationService = new DelegationAdminServiceImpl(); + private final ReferralAdminServiceImpl referralAdminService = new ReferralAdminServiceImpl(); private final NodeRef node1 = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "node1"); private final NodeRef node2 = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "node2"); @@ -63,66 +63,66 @@ public class DelegationUnitTest { MockitoAnnotations.initMocks(this); - metadataDelegationService.setNodeService(mockNodeService); + referralAdminService.setNodeService(mockNodeService); } - @Test public void nullOrEmptyDelegatesAreForbidden() + @Test public void nullOrEmptyReferralsAreForbidden() { - List invalidDelegations = asListFrom(() -> new Delegation(), + List invalidMetadataReferrals = asListFrom(() -> new MetadataReferral(), () -> { - Delegation d = new Delegation(); - d.setAssocType(assoc1); - d.setAspects(null); - d.setDictionaryService(mockDictionaryService); - return d; + MetadataReferral mr = new MetadataReferral(); + mr.setAssocType(assoc1); + mr.setAspects(null); + mr.setDictionaryService(mockDictionaryService); + return mr; }, () -> { - Delegation d = new Delegation(); - d.setAssocType(assoc1); - d.setAspects(emptySet()); - d.setDictionaryService(mockDictionaryService); - return d; + MetadataReferral mr = new MetadataReferral(); + mr.setAssocType(assoc1); + mr.setAspects(emptySet()); + mr.setDictionaryService(mockDictionaryService); + return mr; }, () -> { - Delegation d = new Delegation(); - d.setAssocType(null); - d.setAspects(asSet(aspect1, aspect2)); - d.setDictionaryService(mockDictionaryService); - return d; + MetadataReferral mr = new MetadataReferral(); + mr.setAssocType(null); + mr.setAspects(asSet(aspect1, aspect2)); + mr.setDictionaryService(mockDictionaryService); + return mr; }); - invalidDelegations.stream() - .forEach(d -> expectedException(InvalidDelegation.class, () -> { - d.validateAndRegister(); + invalidMetadataReferrals.stream() + .forEach(mr -> expectedException(InvalidMetadataReferral.class, () -> { + mr.validateAndRegister(); return null; } )); } - @Test(expected=InvalidDelegation.class) - public void delegateMustHaveAssocThatExists() + @Test(expected=InvalidMetadataReferral.class) + public void referralMustHaveAssocThatExists() { when(mockDictionaryService.getAssociation(assoc1)).thenReturn(null); when(mockDictionaryService.getAspect(aspect1)).thenReturn(mock(AspectDefinition.class)); - Delegation d = new Delegation(); - d.setAssocType(assoc1); - d.setAspects(asSet(aspect1)); - d.setDictionaryService(mockDictionaryService); - d.validateAndRegister(); + MetadataReferral mr = new MetadataReferral(); + mr.setAssocType(assoc1); + mr.setAspects(asSet(aspect1)); + mr.setDictionaryService(mockDictionaryService); + mr.validateAndRegister(); } - @Test(expected=InvalidDelegation.class) - public void delegateMustHaveAspectsAllOfWhichExist() + @Test(expected=InvalidMetadataReferral.class) + public void referralMustHaveAspectsAllOfWhichExist() { when(mockDictionaryService.getAssociation(assoc1)).thenReturn(mock(AssociationDefinition.class)); when(mockDictionaryService.getAspect(aspect1)).thenReturn(mock(AspectDefinition.class)); when(mockDictionaryService.getAspect(aspect2)).thenReturn(null); - Delegation d = new Delegation(); - d.setAssocType(assoc1); - d.setAspects(asSet(aspect1, aspect2)); - d.setDictionaryService(mockDictionaryService); - d.validateAndRegister(); + MetadataReferral mr = new MetadataReferral(); + mr.setAssocType(assoc1); + mr.setAspects(asSet(aspect1, aspect2)); + mr.setDictionaryService(mockDictionaryService); + mr.validateAndRegister(); } } diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationAdminServiceImplUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralAdminServiceImplUnitTest.java similarity index 60% rename from rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationAdminServiceImplUnitTest.java rename to rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralAdminServiceImplUnitTest.java index 1cd6a81caf..6e31f011df 100644 --- a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/metadatadelegation/DelegationAdminServiceImplUnitTest.java +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralAdminServiceImplUnitTest.java @@ -16,10 +16,10 @@ * 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.metadatadelegation; +package org.alfresco.module.org_alfresco_module_rm.referredmetadata; import static java.util.Arrays.asList; -import static org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationException.ChainedDelegationUnsupported; +import static org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferredMetadataException.ChainedMetadataReferralUnsupported; import static org.alfresco.module.org_alfresco_module_rm.test.util.ExceptionUtils.expectedException; import static org.alfresco.module.org_alfresco_module_rm.test.util.FPUtils.asSet; import static org.junit.Assert.assertEquals; @@ -40,37 +40,37 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; /** - * Unit tests for {@link DelegationAdminServiceImpl}. + * Unit tests for {@link ReferralAdminServiceImpl}. * * @author Neil Mc Erlean - * @since 3.0.a + * @since 2.4.a */ -public class DelegationAdminServiceImplUnitTest +public class ReferralAdminServiceImplUnitTest { - @InjectMocks private final DelegationAdminServiceImpl delegationAdminService = new DelegationAdminServiceImpl(); + @InjectMocks private final ReferralAdminServiceImpl referralAdminService = new ReferralAdminServiceImpl(); - @Mock DictionaryService mockDictionaryService; - @Mock NodeService mockNodeService; - @Mock DelegationRegistry mockRegistry; - @Mock DelegationServiceImpl mockDelegationService; + @Mock DictionaryService mockDictionaryService; + @Mock NodeService mockNodeService; + @Mock ReferralRegistry mockRegistry; + @Mock ReferredMetadataServiceImpl mockReferredMetadataService; private final NodeRef node1 = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "node1"); private final NodeRef node2 = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "node2"); private final NodeRef node3 = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "node3"); private final QName assoc1 = QName.createQName("test", "assoc1"); - private final QName assoc2 = QName.createQName("test", "assoc2"); - private final QName aspect1 = QName.createQName("test", "aspect1"); private final QName aspect2 = QName.createQName("test", "aspect2"); + + private final QName assoc2 = QName.createQName("test", "assoc2"); private final QName aspect3 = QName.createQName("test", "aspect3"); - private final Delegation delegate1 = new Delegation() + private final MetadataReferral referral1 = new MetadataReferral() {{ this.setAssocType(assoc1); this.setAspects(asSet(aspect1, aspect2)); }}; - private final Delegation delegate2 = new Delegation() + private final MetadataReferral referral2 = new MetadataReferral() {{ this.setAssocType(assoc2); this.setAspects(asSet(aspect3)); @@ -80,61 +80,61 @@ public class DelegationAdminServiceImplUnitTest { MockitoAnnotations.initMocks(this); - when(mockRegistry.getDelegations()).thenReturn(asSet(delegate1, delegate2)); + when(mockRegistry.getMetadataReferrals()).thenReturn(asSet(referral1, referral2)); } @Test(expected=IllegalArgumentException.class) - public void attachingDelegateWithNoAssociationConfiguredShouldFail() + public void attachingReferrerWithNoAssociationConfiguredShouldFail() { - delegationAdminService.attachDelegate(node1, node2, assoc1); + referralAdminService.attachReferrer(node2, node1, assoc1); } @Test public void attachDetach() { - when(mockRegistry.getDelegateForAssociation(assoc1)).thenReturn(delegate1); + when(mockRegistry.getReferralForAssociation(assoc1)).thenReturn(referral1); // attach - Delegation d = attachDelegate(node1, node2, assoc1); + MetadataReferral d = attachReferrer(node1, node2, assoc1); // validate assertEquals(assoc1, d.getAssocType()); assertEquals(asSet(aspect1, aspect2), d.getAspects()); - assertTrue(mockDelegationService.hasDelegateForAspect(node1, aspect1)); - assertFalse(mockDelegationService.hasDelegateForAspect(node1, aspect3)); + assertTrue(mockReferredMetadataService.isReferringMetadata(node1, aspect1)); + assertFalse(mockReferredMetadataService.isReferringMetadata(node1, aspect3)); // detach - assertEquals(d, delegationAdminService.detachDelegate(node1, assoc1)); + assertEquals(d, referralAdminService.detachReferrer(node1, assoc1)); } - private Delegation attachDelegate(NodeRef from, NodeRef to, QName assocType) + private MetadataReferral attachReferrer(NodeRef referrer, NodeRef referent, QName assocType) { - Delegation d = delegationAdminService.attachDelegate(from, to, assocType); - when(mockNodeService.getSourceAssocs(to, assocType)).thenReturn(asList(new AssociationRef(from, assocType, to))); - when(mockNodeService.getTargetAssocs(from, assocType)).thenReturn(asList(new AssociationRef(from, assocType, to))); + MetadataReferral d = referralAdminService.attachReferrer(referrer, referent, assocType); + when(mockNodeService.getSourceAssocs(referent, assocType)).thenReturn(asList(new AssociationRef(referrer, assocType, referent))); + when(mockNodeService.getTargetAssocs(referrer, assocType)).thenReturn(asList(new AssociationRef(referrer, assocType, referent))); for (QName aspect : d.getAspects()) { - when(mockDelegationService.hasDelegateForAspect(from, aspect)).thenReturn(true); + when(mockReferredMetadataService.isReferringMetadata(referrer, aspect)).thenReturn(true); } return d; } @Test public void chainsOfDelegationShouldBePrevented() { - when(mockRegistry.getDelegateForAssociation(assoc1)).thenReturn(delegate1); + when(mockRegistry.getReferralForAssociation(assoc1)).thenReturn(referral1); // The node already has a delegation in place: node1 -> node2. We're trying to add to the // end of the chain: node2 -> node3 when(mockNodeService.getSourceAssocs(node2, assoc1)).thenReturn(asList(new AssociationRef(node1, assoc1, node2))); when(mockNodeService.getTargetAssocs(node1, assoc1)).thenReturn(asList(new AssociationRef(node1, assoc1, node2))); - expectedException(ChainedDelegationUnsupported.class, () -> { - delegationAdminService.attachDelegate(node2, node3, assoc1); + expectedException(ChainedMetadataReferralUnsupported.class, () -> { + referralAdminService.attachReferrer(node2, node3, assoc1); return null; }); // Now try to add to the start of the chain: node3 -> node1 - expectedException(ChainedDelegationUnsupported.class, () -> { - delegationAdminService.attachDelegate(node3, node1, assoc1); + expectedException(ChainedMetadataReferralUnsupported.class, () -> { + referralAdminService.attachReferrer(node3, node1, assoc1); return null; }); } diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferredMetadataServiceImplUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferredMetadataServiceImplUnitTest.java new file mode 100644 index 0000000000..e04a96d087 --- /dev/null +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferredMetadataServiceImplUnitTest.java @@ -0,0 +1,195 @@ +/* + * 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.referredmetadata; + +import static java.util.Collections.emptyMap; +import static java.util.Arrays.asList; +import static org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferredMetadataException.ReferentNodeNotFound; +import static org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferredMetadataException.MetadataReferralNotFound; +import static org.alfresco.module.org_alfresco_module_rm.test.util.ExceptionUtils.expectedException; +import static org.alfresco.module.org_alfresco_module_rm.test.util.FPUtils.asSet; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.when; + +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.QName; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +/** + * Unit tests for {@link ReferredMetadataServiceImpl}. + * + * @author Neil Mc Erlean + * @since 3.0.a + */ +public class ReferredMetadataServiceImplUnitTest +{ + @InjectMocks private final ReferredMetadataServiceImpl referredMetadataService = new ReferredMetadataServiceImpl(); + + @Mock DictionaryService mockDictionaryService; + @Mock NodeService mockNodeService; + @Mock ReferralAdminServiceImpl mockReferralAdminService; + + /** This node has a referent node. */ + private final NodeRef referringNode = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "referringNode"); + /** This is the referent for {@link #referringNode}. */ + private final NodeRef referentNode = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "referentNode"); + /** This node has no referent node. */ + private final NodeRef nodeWithoutReferent = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "nodeWithoutReferent"); + + /** The type of the peer association that links the referringNode to its source. */ + private final QName referralAssocType = QName.createQName("test", "referralAssocType"); + /** The instance of the association between {@link #referringNode} and {@link #referentNode}. */ + private final AssociationRef attachedReferralAssocRef = new AssociationRef(referringNode, referralAssocType, referentNode); + + /** Name of an aspect that has been referred. */ + private final QName referredAspect1 = QName.createQName("test", "referredAspect1"); + /** Name of an aspect that has been referred. */ + private final QName referredAspect2 = QName.createQName("test", "referredAspect2"); + /** Name of a content class (a type in this case) that has not been referred. + * N.B. Types can't be referred currently. */ + private final QName unreferredType = QName.createQName("test", "unreferredType"); + + private final QName referredProp = QName.createQName("test", "referredProp"); + private final Serializable referredPropValue = "hello"; + private final QName unreferredProp = QName.createQName("test", "unreferredProp"); + + private final MetadataReferral referral = new MetadataReferral() + {{ + this.setAssocType(referralAssocType); + this.setAspects(asSet(referredAspect1, referredAspect2)); + }}; + + @Before public void setUp() + { + MockitoAnnotations.initMocks(this); + + final PropertyDefinition aspectProp = mock(PropertyDefinition.class); + final ClassDefinition aspectDefn = mock(ClassDefinition.class); + when(aspectDefn.getName()).thenReturn(referredAspect1); + when(aspectProp.getContainerClass()).thenReturn(aspectDefn); + when(aspectDefn.isAspect()).thenReturn(true); + + final PropertyDefinition typeProp = mock(PropertyDefinition.class); + final ClassDefinition typeDefn = mock(ClassDefinition.class); + when(typeDefn.getName()).thenReturn(unreferredType); + when(typeProp.getContainerClass()).thenReturn(typeDefn); + when(typeDefn.isAspect()).thenReturn(false); + + when(mockDictionaryService.getProperty(referredProp)).thenReturn(aspectProp); + + when(mockReferralAdminService.getAttachedReferralsFrom(referringNode)).thenReturn(asSet(referral)); + for (QName referredAspect : asSet(referredAspect1, referredAspect2)) + { + when(mockReferralAdminService.getReferralFor(referredAspect)).thenReturn(referral); + when(mockNodeService.hasAspect(referentNode, referredAspect)).thenReturn(true); + } + when(mockNodeService.getSourceAssocs(referentNode, referralAssocType)).thenReturn(asList(attachedReferralAssocRef)); + when(mockNodeService.getTargetAssocs(referringNode, referralAssocType)).thenReturn(asList(attachedReferralAssocRef)); + when(mockNodeService.exists(any(NodeRef.class))).thenReturn(true); + when(mockNodeService.getProperties(referentNode)) + .thenReturn(new HashMap() + {{ + this.put(referredProp, referredPropValue); + }}); + } + + @Test public void isReferringMetadata() + { + assertTrue(referredMetadataService.isReferringMetadata(referringNode, referredAspect1)); + expectedException(MetadataReferralNotFound.class, + () -> referredMetadataService.isReferringMetadata(nodeWithoutReferent, unreferredType)); + assertFalse(referredMetadataService.isReferringMetadata(nodeWithoutReferent, referredAspect1)); + } + + @Test public void getReferentNode() + { + assertEquals(referentNode, referredMetadataService.getReferentNode(referringNode, referredAspect1)); + expectedException(MetadataReferralNotFound.class, + () -> { + referredMetadataService.getReferentNode(referringNode, unreferredType); + return null; + }); + assertNull(referredMetadataService.getReferentNode(nodeWithoutReferent, referredAspect1)); + } + + @Test public void getReferredProperties() + { + final Map expectedProps = new HashMap<>(); + expectedProps.put(referredProp, referredPropValue); + + assertEquals(expectedProps, referredMetadataService.getReferredProperties(referringNode, referredAspect1)); + + expectedException(MetadataReferralNotFound.class, + () -> referredMetadataService.getReferredProperties(referringNode, unreferredType)); + + expectedException(ReferentNodeNotFound.class, + () -> referredMetadataService.getReferredProperties(nodeWithoutReferent, referredAspect1)); + } + + @Test public void getReferredProperty() + { + assertEquals(referredPropValue, referredMetadataService.getReferredProperty(referringNode, referredProp)); + + expectedException(IllegalArgumentException.class, + () -> referredMetadataService.getReferredProperty(referringNode, unreferredProp)); + + expectedException(MetadataReferralNotFound.class, + () -> referredMetadataService.getReferredProperties(nodeWithoutReferent, referredProp)); + } + + @Test public void hasReferredAspect() + { + assertTrue(referredMetadataService.hasReferredAspect(referringNode, referredAspect1)); + + expectedException(MetadataReferralNotFound.class, + () -> referredMetadataService.hasReferredAspect(referringNode, unreferredType)); + + expectedException(ReferentNodeNotFound.class, + () -> referredMetadataService.hasReferredAspect(nodeWithoutReferent, referredAspect1)); + } + + @Test public void getAttachedReferrals() + { + final Map expectedReferrals = new HashMap<>(); + expectedReferrals.put(referral, referentNode); + + assertEquals(expectedReferrals, referredMetadataService.getAttachedReferrals(referringNode)); + assertEquals(emptyMap(), referredMetadataService.getAttachedReferrals(nodeWithoutReferent)); + } +} From 1e84916582ce696ffb20c8859dfa1fb8087a0eb5 Mon Sep 17 00:00:00 2001 From: Neil McErlean Date: Tue, 8 Sep 2015 15:32:10 +0000 Subject: [PATCH 05/12] This is the RM-specific parts of the refactor for classified renditions - see RM-2549. This builds on the separate MetadataReferral services to link renditions to classified source nodes via a new classifiedRendition aspect which defines a new classifiedRendition assoc. The spring bean 'classifiedRenditionAssoc' defines that only the clf:classified aspect and its metadata will be linked. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/BRANCHES/classified_renditions@111643 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../classified-content-context.xml | 13 ++- .../model/classifiedContentModel.xml | 21 +++- .../ContentClassificationServiceImpl.java | 49 +++++++--- .../model/ClassifiedContentModel.java | 4 + .../model/clf/ClassifiedRenditions.java | 22 +++-- .../model/clf/aspect/ClassifiedAspect.java | 69 +++++++------- ...tentClassificationServiceImplUnitTest.java | 2 + .../clf/ClassifiedRenditionsUnitTest.java | 95 ------------------- .../clf/aspect/ClassifiedAspectUnitTest.java | 62 +++--------- 9 files changed, 126 insertions(+), 211 deletions(-) delete mode 100644 rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/model/clf/ClassifiedRenditionsUnitTest.java diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/classified-content-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/classified-content-context.xml index 868d68578d..f71c08977a 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/classified-content-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/classified-content-context.xml @@ -125,6 +125,14 @@ + + + + + clf:classified + + + @@ -187,6 +195,7 @@ + @@ -234,15 +243,15 @@ class="org.alfresco.module.org_alfresco_module_rm.model.clf.aspect.ClassifiedAspect" parent="rm.baseBehaviour"> + - + - diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/model/classifiedContentModel.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/model/classifiedContentModel.xml index 88b17a8445..8cefb0d377 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/model/classifiedContentModel.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/model/classifiedContentModel.xml @@ -38,11 +38,6 @@ type="org.alfresco.module.org_alfresco_module_rm.classification.ReclassificationValueConstraint" /> - - - - - @@ -178,5 +173,21 @@ + + + + + + true + true + + + cm:content + true + true + + + + \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ContentClassificationServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ContentClassificationServiceImpl.java index 4f56eccd7f..bff670b639 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ContentClassificationServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ContentClassificationServiceImpl.java @@ -23,11 +23,6 @@ import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.c import static org.alfresco.util.ParameterCheck.mandatory; import static org.apache.commons.lang3.StringUtils.isNotBlank; -import java.io.Serializable; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; - import org.alfresco.model.ContentModel; import org.alfresco.model.QuickShareModel; import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationException.InvalidNode; @@ -35,6 +30,7 @@ import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationE import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationException.ReasonIdNotFound; import org.alfresco.module.org_alfresco_module_rm.classification.model.ClassifiedContentModel; import org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService; +import org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferredMetadataService; import org.alfresco.module.org_alfresco_module_rm.util.ServiceBaseImpl; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; @@ -43,6 +39,11 @@ import org.alfresco.service.cmr.repository.InvalidNodeRefException; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; +import java.io.Serializable; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + /** * A service to handle the classification of content. * @@ -57,12 +58,17 @@ public class ContentClassificationServiceImpl extends ServiceBaseImpl private SecurityClearanceService securityClearanceService; private ClassificationServiceBootstrap classificationServiceBootstrap; private FreezeService freezeService; + private ReferredMetadataService referredMetadataService; public void setLevelManager(ClassificationLevelManager levelManager) { this.levelManager = levelManager; } public void setReasonManager(ClassificationReasonManager reasonManager) { this.reasonManager = reasonManager; } public void setSecurityClearanceService(SecurityClearanceService securityClearanceService) { this.securityClearanceService = securityClearanceService; } public void setClassificationServiceBootstrap(ClassificationServiceBootstrap classificationServiceBootstrap) { this.classificationServiceBootstrap = classificationServiceBootstrap; } public void setFreezeService(FreezeService service) { this.freezeService = service; } + public void setReferredMetadataService(ReferredMetadataService service) + { + this.referredMetadataService = service; + } public void init() { @@ -80,16 +86,24 @@ public class ContentClassificationServiceImpl extends ServiceBaseImpl { public ClassificationLevel doWork() throws Exception { - // by default everything is unclassified - ClassificationLevel result = ClassificationLevelManager.UNCLASSIFIED; + final String classificationId; if (nodeService.hasAspect(nodeRef, ASPECT_CLASSIFIED)) { - String classificationId = (String)nodeService.getProperty(nodeRef, PROP_CURRENT_CLASSIFICATION); - result = levelManager.findLevelById(classificationId); + classificationId = (String)nodeService.getProperty(nodeRef, PROP_CURRENT_CLASSIFICATION); + } + else if (referredMetadataService.isReferringMetadata(nodeRef, ASPECT_CLASSIFIED)) + { + classificationId = (String) referredMetadataService.getReferredProperty(nodeRef, PROP_CURRENT_CLASSIFICATION); + // Note that this property value could be null/missing. + } + else + { + classificationId = null; } - return result; + // by default everything is unclassified + return classificationId == null ? ClassificationLevelManager.UNCLASSIFIED : levelManager.findLevelById(classificationId); } }); }; @@ -231,12 +245,19 @@ public class ContentClassificationServiceImpl extends ServiceBaseImpl mandatory("nodeRef", nodeRef); boolean isClassified = false; - String currentClassification = (String) nodeService.getProperty(nodeRef, PROP_CURRENT_CLASSIFICATION); - if (nodeService.hasAspect(nodeRef, ASPECT_CLASSIFIED) && - !(UNCLASSIFIED_ID).equals(currentClassification)) + final String currentClassification; + if (nodeService.hasAspect(nodeRef, ASPECT_CLASSIFIED)) { - isClassified = true; + currentClassification = (String) nodeService.getProperty(nodeRef, PROP_CURRENT_CLASSIFICATION); + isClassified = currentClassification != null && ! UNCLASSIFIED_ID.equals(currentClassification); + } + else if (referredMetadataService.isReferringMetadata(nodeRef, ASPECT_CLASSIFIED)) + { + currentClassification = (String) referredMetadataService.getReferredProperty(nodeRef, PROP_CURRENT_CLASSIFICATION); + // This could be a null/missing property. + + isClassified = currentClassification != null && ! UNCLASSIFIED_ID.equals(currentClassification); } return isClassified; diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/model/ClassifiedContentModel.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/model/ClassifiedContentModel.java index be45c49d28..66773d8c01 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/model/ClassifiedContentModel.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/model/ClassifiedContentModel.java @@ -62,4 +62,8 @@ public interface ClassifiedContentModel /** Security Clearance aspect. */ QName ASPECT_SECURITY_CLEARANCE = QName.createQName(CLF_URI, "securityClearance"); QName PROP_CLEARANCE_LEVEL = QName.createQName(CLF_URI, "clearanceLevel"); + + /** Classified Rendition aspect. */ + QName ASPECT_CLASSIFIED_RENDITION = QName.createQName(CLF_URI, "classifiedRendition"); + QName ASSOC_CLASSIFIED_RENDITION = QName.createQName(CLF_URI, "classifiedRendition"); } \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/clf/ClassifiedRenditions.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/clf/ClassifiedRenditions.java index 84fc9fe741..1f92b82cb0 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/clf/ClassifiedRenditions.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/clf/ClassifiedRenditions.java @@ -20,21 +20,22 @@ package org.alfresco.module.org_alfresco_module_rm.model.clf; import org.alfresco.module.org_alfresco_module_rm.classification.ContentClassificationService; import org.alfresco.module.org_alfresco_module_rm.classification.model.ClassifiedContentModel; +import org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferralAdminService; import org.alfresco.module.org_alfresco_module_rm.model.BaseBehaviourBean; -import org.alfresco.module.org_alfresco_module_rm.util.CoreServicesExtras; import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.policy.Behaviour.NotificationFrequency; import org.alfresco.repo.policy.annotation.Behaviour; import org.alfresco.repo.policy.annotation.BehaviourBean; import org.alfresco.repo.policy.annotation.BehaviourKind; import org.alfresco.service.cmr.rendition.RenditionService; +import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; /** * Behaviour bean for classified rendition nodes. * - * @since 3.0.a + * @since 2.4.a */ @BehaviourBean ( @@ -45,7 +46,7 @@ public class ClassifiedRenditions extends BaseBehaviourBean ClassifiedContentModel { private ContentClassificationService contentClassificationService; - private CoreServicesExtras servicesExtras; + private ReferralAdminService referralAdminService; private RenditionService renditionService; public void setContentClassificationService(ContentClassificationService service) @@ -53,9 +54,9 @@ public class ClassifiedRenditions extends BaseBehaviourBean this.contentClassificationService = service; } - public void setCoreServicesExtras(CoreServicesExtras extras) + public void setReferralAdminService(ReferralAdminService service) { - this.servicesExtras = extras; + this.referralAdminService = service; } public void setRenditionService(RenditionService service) @@ -74,16 +75,19 @@ public class ClassifiedRenditions extends BaseBehaviourBean ) public void onAddAspect(final NodeRef renditionNodeRef, final QName aspectTypeQName) { + // When a rendition is created, set up a metadata link of its classification to the source node. authenticationUtil.runAs(new org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork() { public Void doWork() { - final NodeRef sourceNode = renditionService.getSourceNode(renditionNodeRef).getParentRef(); - if (contentClassificationService.isClassified(sourceNode)) + final ChildAssociationRef chAssRef = renditionService.getSourceNode(renditionNodeRef); + final NodeRef sourceNode = chAssRef.getParentRef(); + if (contentClassificationService.isClassified(sourceNode) && + referralAdminService.getAttachedReferralFrom(renditionNodeRef, ASPECT_CLASSIFIED) != null) { - // All renditions should be given the same classification as their source node - servicesExtras.copyAspect(sourceNode, renditionNodeRef, ASPECT_CLASSIFIED); + referralAdminService.attachReferrer(renditionNodeRef, sourceNode, ASSOC_CLASSIFIED_RENDITION); } + return null; } }, authenticationUtil.getSystemUserName()); diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/clf/aspect/ClassifiedAspect.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/clf/aspect/ClassifiedAspect.java index 97f71efddd..91cfee419a 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/clf/aspect/ClassifiedAspect.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/clf/aspect/ClassifiedAspect.java @@ -20,17 +20,13 @@ package org.alfresco.module.org_alfresco_module_rm.model.clf.aspect; import static org.alfresco.module.org_alfresco_module_rm.util.RMCollectionUtils.diffKey; -import java.io.Serializable; -import java.util.Date; -import java.util.Map; - import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationException.MissingDowngradeInstructions; import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationLevel; import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationSchemeService; import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationSchemeService.Reclassification; import org.alfresco.module.org_alfresco_module_rm.classification.model.ClassifiedContentModel; +import org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferralAdminService; import org.alfresco.module.org_alfresco_module_rm.model.BaseBehaviourBean; -import org.alfresco.module.org_alfresco_module_rm.util.CoreServicesExtras; import org.alfresco.module.org_alfresco_module_rm.util.RMCollectionUtils.Difference; import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.policy.Behaviour.NotificationFrequency; @@ -44,10 +40,15 @@ import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; +import java.io.Serializable; +import java.util.Date; +import java.util.List; +import java.util.Map; + /** * clf:classification behaviour bean * - * @since 3.0.a + * @since 2.4.a */ @BehaviourBean ( @@ -58,31 +59,27 @@ public class ClassifiedAspect extends BaseBehaviourBean implements NodeServicePo ClassifiedContentModel { private ClassificationSchemeService classificationSchemeService; + private ReferralAdminService referralAdminService; private RenditionService renditionService; - private CoreServicesExtras servicesExtras; public void setClassificationSchemeService(ClassificationSchemeService service) { this.classificationSchemeService = service; } + public void setReferralAdminService(ReferralAdminService service) + { + this.referralAdminService = service; + } + public void setRenditionService(RenditionService service) { this.renditionService = service; } - public void setCoreServicesExtras(CoreServicesExtras extras) - { - this.servicesExtras = extras; - } - /** * Behaviour associated with updating the classified aspect properties. *

- * Ensures that on reclassification of content (in other words a change in the value of the - * {@link ClassifiedContentModel#PROP_CURRENT_CLASSIFICATION clf:currentClassification} property) - * that various metadata are correctly updated as a side-effect. - *

* Validates the consistency of the properties. */ @Override @@ -91,7 +88,7 @@ public class ClassifiedAspect extends BaseBehaviourBean implements NodeServicePo kind = BehaviourKind.CLASS, notificationFrequency = NotificationFrequency.EVERY_EVENT ) - public void onUpdateProperties(final NodeRef nodeRef, + public void onUpdateProperties(final NodeRef classifiedNode, final Map before, final Map after) { @@ -101,7 +98,7 @@ public class ClassifiedAspect extends BaseBehaviourBean implements NodeServicePo { final Difference classificationChange = diffKey(before, after, PROP_CURRENT_CLASSIFICATION); - if (classificationChange == Difference.CHANGED && nodeService.hasAspect(nodeRef, ASPECT_CLASSIFIED)) + if (classificationChange == Difference.CHANGED && nodeService.hasAspect(classifiedNode, ASPECT_CLASSIFIED)) { final String oldValue = (String)before.get(PROP_CURRENT_CLASSIFICATION); final String newValue = (String)after.get(PROP_CURRENT_CLASSIFICATION); @@ -113,14 +110,12 @@ public class ClassifiedAspect extends BaseBehaviourBean implements NodeServicePo if (reclassification != null) { - nodeService.setProperty(nodeRef, PROP_LAST_RECLASSIFICATION_ACTION, reclassification.toModelString()); - nodeService.setProperty(nodeRef, PROP_LAST_RECLASSIFY_AT, new Date()); + nodeService.setProperty(classifiedNode, PROP_LAST_RECLASSIFICATION_ACTION, reclassification.toModelString()); + nodeService.setProperty(classifiedNode, PROP_LAST_RECLASSIFY_AT, new Date()); } } - checkConsistencyOfProperties(nodeRef); - - copyClassifiedPropertiesToRenditions(nodeRef); + checkConsistencyOfProperties(classifiedNode); return null; } @@ -136,33 +131,33 @@ public class ClassifiedAspect extends BaseBehaviourBean implements NodeServicePo @Behaviour ( kind = BehaviourKind.CLASS, - notificationFrequency = NotificationFrequency.EVERY_EVENT + notificationFrequency = NotificationFrequency.FIRST_EVENT ) - public void onAddAspect(final NodeRef nodeRef, final QName aspectTypeQName) + public void onAddAspect(final NodeRef classifiedNode, final QName aspectTypeQName) { AuthenticationUtil.runAs(new RunAsWork() { public Void doWork() { - checkConsistencyOfProperties(nodeRef); + checkConsistencyOfProperties(classifiedNode); - copyClassifiedPropertiesToRenditions(nodeRef); + // If this node has any renditions, we must ensure that they inherit the classification + // from their source node. + final List renditions = renditionService.getRenditions(classifiedNode); + for (ChildAssociationRef chAssRef : renditions) + { + final NodeRef renditionNode = chAssRef.getChildRef(); + if (referralAdminService.getAttachedReferralFrom(renditionNode, ASPECT_CLASSIFIED_RENDITION) == null) + { + referralAdminService.attachReferrer(renditionNode, classifiedNode, ASSOC_CLASSIFIED_RENDITION); + } + } return null; } }, AuthenticationUtil.getSystemUserName()); } - private void copyClassifiedPropertiesToRenditions(NodeRef nodeRef) - { - // All renditions should be given the same classification as their source node - for (final ChildAssociationRef chAssRef : renditionService.getRenditions(nodeRef)) - { - final NodeRef renditionNode = chAssRef.getChildRef(); - servicesExtras.copyAspect(nodeRef, renditionNode, ASPECT_CLASSIFIED); - } - } - /** * Check the consistency of the classification properties and throw an exception if they are invalid. * diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/classification/ContentClassificationServiceImplUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/classification/ContentClassificationServiceImplUnitTest.java index bc141bcc5c..b708aeb96a 100644 --- a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/classification/ContentClassificationServiceImplUnitTest.java +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/classification/ContentClassificationServiceImplUnitTest.java @@ -45,6 +45,7 @@ import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationE import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationException.LevelIdNotFound; import org.alfresco.module.org_alfresco_module_rm.classification.model.ClassifiedContentModel; import org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService; +import org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferredMetadataService; import org.alfresco.module.org_alfresco_module_rm.test.util.MockAuthenticationUtilHelper; import org.alfresco.module.org_alfresco_module_rm.util.AuthenticationUtil; import org.alfresco.repo.security.permissions.AccessDeniedException; @@ -80,6 +81,7 @@ public class ContentClassificationServiceImplUnitTest implements ClassifiedConte @Mock FreezeService mockFreezeService; @Mock SecurityClearanceService mockSecurityClearanceService; @Mock AuthenticationUtil mockAuthenticationUtil; + @Mock ReferredMetadataService mockReferredMetadataService; @Mock ClassificationAspectProperties mockPropertiesDTO; @Captor ArgumentCaptor> propertiesCaptor; diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/model/clf/ClassifiedRenditionsUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/model/clf/ClassifiedRenditionsUnitTest.java deleted file mode 100644 index da48e63539..0000000000 --- a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/model/clf/ClassifiedRenditionsUnitTest.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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.model.clf; - -import static java.util.Arrays.asList; - -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.initMocks; - -import org.alfresco.model.RenditionModel; -import org.alfresco.module.org_alfresco_module_rm.classification.ContentClassificationService; -import org.alfresco.module.org_alfresco_module_rm.classification.model.ClassifiedContentModel; -import org.alfresco.module.org_alfresco_module_rm.model.clf.aspect.ClassifiedAspect; -import org.alfresco.module.org_alfresco_module_rm.test.util.MockAuthenticationUtilHelper; -import org.alfresco.module.org_alfresco_module_rm.util.AuthenticationUtil; -import org.alfresco.module.org_alfresco_module_rm.util.CoreServicesExtras; -import org.alfresco.service.cmr.rendition.RenditionService; -import org.alfresco.service.cmr.repository.ChildAssociationRef; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.junit.Before; -import org.junit.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; - -/** - * Unit tests for {@link ClassifiedRenditions}. - * - * @since 3.0.a - */ -public class ClassifiedRenditionsUnitTest implements ClassifiedContentModel -{ - private static final NodeRef SOURCE_NODE = new NodeRef("node://ref/"); - private static final NodeRef RENDITION_1 = new NodeRef("node://rendition1/"); - private static final NodeRef RENDITION_2 = new NodeRef("node://rendition2/"); - - @InjectMocks ClassifiedAspect classifiedAspect; - - @Mock AuthenticationUtil mockAuthenticationUtil; - @Mock ContentClassificationService mockContentClassificationService; - @Mock CoreServicesExtras mockCoreServicesExtras; - @Mock NodeService mockNodeService; - @Mock RenditionService mockRenditionService; - - @Before - public void setUp() - { - initMocks(this); - - MockAuthenticationUtilHelper.setup(mockAuthenticationUtil); - } - - @Test public void newRenditionOfClassifiedNodeShouldItselfBeClassified() - { - when(mockRenditionService.getRenditions(SOURCE_NODE)) - .thenReturn(asList(rendition(SOURCE_NODE, RENDITION_1), rendition(SOURCE_NODE, RENDITION_2))); - when(mockRenditionService.getSourceNode(RENDITION_1)).thenReturn(rendition(SOURCE_NODE, RENDITION_1)); - when(mockRenditionService.getSourceNode(RENDITION_2)).thenReturn(rendition(SOURCE_NODE, RENDITION_2)); - when(mockContentClassificationService.isClassified(SOURCE_NODE)).thenReturn(true); - - final ClassifiedRenditions behaviour = new ClassifiedRenditions(); - behaviour.setAuthenticationUtil(mockAuthenticationUtil); - behaviour.setContentClassificationService(mockContentClassificationService); - behaviour.setCoreServicesExtras(mockCoreServicesExtras); - behaviour.setNodeService(mockNodeService); - behaviour.setRenditionService(mockRenditionService); - - behaviour.onAddAspect(RENDITION_2, RenditionModel.ASPECT_RENDITION); - - verify(mockCoreServicesExtras).copyAspect(SOURCE_NODE, RENDITION_2, ClassifiedContentModel.ASPECT_CLASSIFIED); - } - - /** Creates a test Rendition ChildAssociationRef. */ - private ChildAssociationRef rendition(NodeRef source, NodeRef rendition) - { - return new ChildAssociationRef(RenditionModel.ASSOC_RENDITION, source, RenditionModel.ASSOC_RENDITION, rendition); - } -} diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/model/clf/aspect/ClassifiedAspectUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/model/clf/aspect/ClassifiedAspectUnitTest.java index 35d7404ac9..30e89eff75 100644 --- a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/model/clf/aspect/ClassifiedAspectUnitTest.java +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/model/clf/aspect/ClassifiedAspectUnitTest.java @@ -19,52 +19,46 @@ package org.alfresco.module.org_alfresco_module_rm.model.clf.aspect; import static java.util.Arrays.asList; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; -import java.io.Serializable; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; - import org.alfresco.model.RenditionModel; import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationException.MissingDowngradeInstructions; -import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationLevel; import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationSchemeService; import org.alfresco.module.org_alfresco_module_rm.classification.model.ClassifiedContentModel; +import org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferralAdminService; import org.alfresco.module.org_alfresco_module_rm.util.CoreServicesExtras; import org.alfresco.service.cmr.rendition.RenditionService; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.namespace.QName; import org.junit.Before; import org.junit.Test; import org.mockito.InjectMocks; import org.mockito.Mock; +import java.util.Date; + /** * Unit tests for the {@link ClassifiedAspect}. * * @author Tom Page - * @since 3.0.a + * @author Neil Mc Erlean + * @since 2.4.a */ public class ClassifiedAspectUnitTest implements ClassifiedContentModel { private static final NodeRef NODE_REF = new NodeRef("node://Ref/"); private static final NodeRef RENDITION_1 = new NodeRef("node://rendition1/"); private static final NodeRef RENDITION_2 = new NodeRef("node://rendition2/"); - private static final ClassificationLevel TOP_SECRET = new ClassificationLevel("Top Secret", "Top Secret"); - private static final ClassificationLevel SECRET = new ClassificationLevel("Secret", "Secret"); @InjectMocks ClassifiedAspect classifiedAspect; @Mock ClassificationSchemeService mockClassificationSchemeService; @Mock CoreServicesExtras mockCoreServicesExtras; @Mock NodeService mockNodeService; @Mock RenditionService mockRenditionService; + @Mock ReferralAdminService mockReferralAdminService; @Before public void setUp() @@ -135,50 +129,20 @@ public class ClassifiedAspectUnitTest implements ClassifiedContentModel classifiedAspect.checkConsistencyOfProperties(NODE_REF); } - /** Check that when a node is classified, its renditions are also classified. */ - @Test public void classificationOfNodeShouldClassifyRenditions() + @Test public void newlyClassifiedNodeShouldLinkItsMetadataToAllRenditions() { - for (NodeRef n : asList(NODE_REF, RENDITION_1, RENDITION_2)) - { - when(mockNodeService.hasAspect(n, ASPECT_CLASSIFIED)).thenReturn(true); - } - when(mockClassificationSchemeService.getClassificationLevelById(eq("Top Secret"))).thenReturn(TOP_SECRET); - when(mockClassificationSchemeService.getClassificationLevelById(eq("Secret"))).thenReturn(SECRET); - when(mockClassificationSchemeService.getReclassification(any(), any())).thenReturn(ClassificationSchemeService.Reclassification.DOWNGRADE); - when(mockRenditionService.getRenditions(eq(NODE_REF))) + when(mockRenditionService.getRenditions(NODE_REF)) .thenReturn(asList(rendition(NODE_REF, RENDITION_1), rendition(NODE_REF, RENDITION_2))); + for (final NodeRef rendition : asList(RENDITION_1, RENDITION_2)) + { + when(mockRenditionService.getSourceNode(rendition)).thenReturn(rendition(NODE_REF, rendition)); + } classifiedAspect.onAddAspect(NODE_REF, ASPECT_CLASSIFIED); for (NodeRef rendition : asList(RENDITION_1, RENDITION_2)) { - verify(mockCoreServicesExtras).copyAspect(NODE_REF, rendition, ClassifiedContentModel.ASPECT_CLASSIFIED); - } - } - - @Test public void reclassificationOfNodeShouldReclassifyRenditions() - { - for (NodeRef n : asList(NODE_REF, RENDITION_1, RENDITION_2)) - { - when(mockNodeService.hasAspect(n, ASPECT_CLASSIFIED)).thenReturn(true); - } - when(mockClassificationSchemeService.getClassificationLevelById("Top Secret")).thenReturn(TOP_SECRET); - when(mockClassificationSchemeService.getClassificationLevelById("Secret")).thenReturn(SECRET); - when(mockClassificationSchemeService.getReclassification(any(), any())).thenReturn(ClassificationSchemeService.Reclassification.DOWNGRADE); - when(mockRenditionService.getRenditions(eq(NODE_REF))) - .thenReturn(asList(rendition(NODE_REF, RENDITION_1), rendition(NODE_REF, RENDITION_2))); - - Map oldProps = new HashMap<>(); - oldProps.put(PROP_CLASSIFIED_BY, "userone"); - oldProps.put(PROP_CURRENT_CLASSIFICATION, "Top Secret"); - Map newProps = new HashMap<>(oldProps); - newProps.put(PROP_CURRENT_CLASSIFICATION, "Secret"); - - classifiedAspect.onUpdateProperties(NODE_REF, oldProps, newProps); - - for (NodeRef rendition : asList(RENDITION_1, RENDITION_2)) - { - verify(mockCoreServicesExtras).copyAspect(NODE_REF, rendition, ClassifiedContentModel.ASPECT_CLASSIFIED); + verify(mockReferralAdminService).attachReferrer(rendition, NODE_REF, ASSOC_CLASSIFIED_RENDITION); } } From d73166c03ba32e601a4493dd69cfa42045d5766c Mon Sep 17 00:00:00 2001 From: Neil McErlean Date: Tue, 8 Sep 2015 16:14:10 +0000 Subject: [PATCH 06/12] Addressing code review comments. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/BRANCHES/classified_renditions@111696 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../test/util/FPUtils.java | 25 ++++++++++--------- .../test/util/FPUtilsUnitTest.java | 2 +- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/FPUtils.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/FPUtils.java index d0e49771f9..335461a587 100644 --- a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/FPUtils.java +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/FPUtils.java @@ -18,6 +18,7 @@ */ package org.alfresco.module.org_alfresco_module_rm.test.util; +import static java.util.Arrays.asList; import static java.util.stream.Collectors.toList; import java.util.Collections; @@ -31,7 +32,7 @@ import java.util.stream.Stream; * Utility class to help with Java 8 FP stuff. * * @author Neil Mc Erlean - * @since 3.0.a + * @since 2.4.a */ public class FPUtils { @@ -41,7 +42,7 @@ public class FPUtils * * @param suppliers a vararg of {@link Supplier}s giving a sequence of values for the list. * @param the type of elements in the list. - * @return the list with each element retrieved from a {@code Supplier}. + * @return the list with each element being the first retrieved from a {@code Supplier}. */ public static List asListFrom(Supplier... suppliers) { @@ -57,12 +58,18 @@ public class FPUtils } } + /** + * This method is intended to work exactly like {@link #asSet(Object[])}} but it takes + * a vararg of {@code Supplier}s instead of actual objects. + * + * @param suppliers a vararg of {@link Supplier}s giving a sequence of values for the set. + * @param the type of elements in the set. + * @return the set with each element being the first retrieved from a {@code Supplier} (duplicates removed). + */ public static Set asSetFrom(Supplier... suppliers) { List l = asListFrom(suppliers); - Set result = new HashSet<>(); - result.addAll(l); - return result; + return new HashSet<>(l); } /** @@ -73,12 +80,6 @@ public class FPUtils */ public static Set asSet(T... objects) { - Set result = new HashSet<>(); - for (T obj : objects) - { - result.add(obj); - } - - return result; + return new HashSet<>(asList(objects)); } } \ No newline at end of file diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/FPUtilsUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/FPUtilsUnitTest.java index 206687295a..c15e92e275 100644 --- a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/FPUtilsUnitTest.java +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/FPUtilsUnitTest.java @@ -34,7 +34,7 @@ import org.junit.Test; * Unit tests for {@link FPUtils}. * * @author Neil Mc Erlean - * @since 3.0.a + * @since 2.4.a */ public class FPUtilsUnitTest { From e8d070f7a26d4ef1ce152cca087c0f5f1b37c962 Mon Sep 17 00:00:00 2001 From: Neil McErlean Date: Tue, 8 Sep 2015 16:21:37 +0000 Subject: [PATCH 07/12] Addressing code review comments git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/BRANCHES/classified_renditions@111703 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../test/util/FPUtilsUnitTest.java | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/FPUtilsUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/FPUtilsUnitTest.java index c15e92e275..8db28f62b3 100644 --- a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/FPUtilsUnitTest.java +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/FPUtilsUnitTest.java @@ -20,6 +20,7 @@ package org.alfresco.module.org_alfresco_module_rm.test.util; import static org.alfresco.module.org_alfresco_module_rm.test.util.FPUtils.asListFrom; import static org.alfresco.module.org_alfresco_module_rm.test.util.FPUtils.asSet; +import static org.alfresco.module.org_alfresco_module_rm.test.util.FPUtils.asSetFrom; import static org.junit.Assert.assertEquals; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; @@ -57,10 +58,20 @@ public class FPUtilsUnitTest @Test public void asSetShouldProduceSet() { - Set expectedSet = new HashSet<>(); - expectedSet.add("hello"); - expectedSet.add("world"); + assertEquals(new HashSet<>(asList("hello", "world")), + asSet("hello", "hello", "world")); + } - assertEquals(expectedSet, asSet("hello", "hello", "world")); + @Test public void asSetFromShouldWork() + { + Set s = asSetFrom(() -> "hello", + () -> "hello", + () -> "world", + () -> { + String s1 = "wo"; + String s2 = "rld"; + return s1 + s2; + }); + assertEquals(new HashSet<>(asList("hello", "world")), s); } } \ No newline at end of file From 1e12436000c08c9edd405a24e9221cb3d0d67c1c Mon Sep 17 00:00:00 2001 From: Neil McErlean Date: Tue, 8 Sep 2015 16:25:47 +0000 Subject: [PATCH 08/12] Addressing review comments. Clearer use of lambdas due to default methods in java.util.Collection. Thanks Tom! git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/BRANCHES/classified_renditions@111707 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../MetadataReferralUnitTest.java | 62 ++++++++----------- 1 file changed, 27 insertions(+), 35 deletions(-) diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/MetadataReferralUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/MetadataReferralUnitTest.java index 847f64f0d1..ea942e7272 100644 --- a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/MetadataReferralUnitTest.java +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/MetadataReferralUnitTest.java @@ -29,17 +29,13 @@ import static org.mockito.Mockito.when; import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.AssociationDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.namespace.QName; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import java.util.List; - /** * Unit tests for {@link MetadataReferral}. * @@ -53,8 +49,6 @@ public class MetadataReferralUnitTest private final ReferralAdminServiceImpl referralAdminService = new ReferralAdminServiceImpl(); - private final NodeRef node1 = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "node1"); - private final NodeRef node2 = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "node2"); private final QName aspect1 = QName.createQName("test", "aspect1"); private final QName aspect2 = QName.createQName("test", "aspect2"); private final QName assoc1 = QName.createQName("test", "assoc1"); @@ -68,35 +62,33 @@ public class MetadataReferralUnitTest @Test public void nullOrEmptyReferralsAreForbidden() { - List invalidMetadataReferrals = asListFrom(() -> new MetadataReferral(), - () -> { - MetadataReferral mr = new MetadataReferral(); - mr.setAssocType(assoc1); - mr.setAspects(null); - mr.setDictionaryService(mockDictionaryService); - return mr; - }, - () -> { - MetadataReferral mr = new MetadataReferral(); - mr.setAssocType(assoc1); - mr.setAspects(emptySet()); - mr.setDictionaryService(mockDictionaryService); - return mr; - }, - () -> { - MetadataReferral mr = new MetadataReferral(); - mr.setAssocType(null); - mr.setAspects(asSet(aspect1, aspect2)); - mr.setDictionaryService(mockDictionaryService); - return mr; - }); - - invalidMetadataReferrals.stream() - .forEach(mr -> expectedException(InvalidMetadataReferral.class, () -> { - mr.validateAndRegister(); - return null; - } - )); + asListFrom(() -> new MetadataReferral(), + () -> { + MetadataReferral mr = new MetadataReferral(); + mr.setAssocType(assoc1); + mr.setAspects(null); + mr.setDictionaryService(mockDictionaryService); + return mr; + }, + () -> { + MetadataReferral mr = new MetadataReferral(); + mr.setAssocType(assoc1); + mr.setAspects(emptySet()); + mr.setDictionaryService(mockDictionaryService); + return mr; + }, + () -> { + MetadataReferral mr = new MetadataReferral(); + mr.setAssocType(null); + mr.setAspects(asSet(aspect1, aspect2)); + mr.setDictionaryService(mockDictionaryService); + return mr; + }) + .forEach(mr -> expectedException(InvalidMetadataReferral.class, () -> { + mr.validateAndRegister(); + return null; + }) + ); } @Test(expected=InvalidMetadataReferral.class) From 2896e7142307c092f92b8ef0696ad892409725b3 Mon Sep 17 00:00:00 2001 From: Neil McErlean Date: Wed, 9 Sep 2015 09:12:02 +0000 Subject: [PATCH 09/12] Addition of tidyup code for when clf:classified aspect is removed. Also added notes on what's to do if this ever becomes a core service. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/BRANCHES/classified_renditions@111768 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../model/clf/aspect/ClassifiedAspect.java | 34 ++++++++++++++++++- .../ReferralAdminServiceImpl.java | 29 ++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/clf/aspect/ClassifiedAspect.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/clf/aspect/ClassifiedAspect.java index 91cfee419a..7730f82875 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/clf/aspect/ClassifiedAspect.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/clf/aspect/ClassifiedAspect.java @@ -56,6 +56,7 @@ import java.util.Map; ) public class ClassifiedAspect extends BaseBehaviourBean implements NodeServicePolicies.OnUpdatePropertiesPolicy, NodeServicePolicies.OnAddAspectPolicy, + NodeServicePolicies.OnRemoveAspectPolicy, ClassifiedContentModel { private ClassificationSchemeService classificationSchemeService; @@ -123,7 +124,7 @@ public class ClassifiedAspect extends BaseBehaviourBean implements NodeServicePo } /** - * Behaviour associated with updating the classified aspect properties. + * Behaviour associated with adding the classified aspect. *

* Validates the consistency of the properties. */ @@ -158,6 +159,37 @@ public class ClassifiedAspect extends BaseBehaviourBean implements NodeServicePo }, AuthenticationUtil.getSystemUserName()); } + /** + * Behaviour associated with removing the classified aspect. + *

+ * Validates the consistency of the properties. + */ + @Override + @Behaviour + ( + kind = BehaviourKind.CLASS, + notificationFrequency = NotificationFrequency.FIRST_EVENT + ) + public void onRemoveAspect(final NodeRef classifiedNode, final QName aspectTypeQName) + { + AuthenticationUtil.runAs(new RunAsWork() + { + public Void doWork() + { + // If this node has any renditions, we should remove the metadata link + final List renditions = renditionService.getRenditions(classifiedNode); + for (ChildAssociationRef chAssRef : renditions) + { + // In RM, renditions are only attached to one metadata referent - the source node. + // Therefore it is safe to (and we must) remove the aspect from the rendition node. + nodeService.removeAspect(chAssRef.getChildRef(), ASPECT_CLASSIFIED_RENDITION); + } + + return null; + } + }, AuthenticationUtil.getSystemUserName()); + } + /** * Check the consistency of the classification properties and throw an exception if they are invalid. * diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralAdminServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralAdminServiceImpl.java index 117261b990..f2b43b1dc9 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralAdminServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralAdminServiceImpl.java @@ -37,6 +37,35 @@ import java.util.Set; */ public class ReferralAdminServiceImpl implements ReferralAdminService { + // Author's implementation note + // ---------------------------- + // + // I can imagine that these services would be potentially useful in core Alfresco. + // However they are not yet full services and couldn't be moved as is into core. + // They solve a very specific RM problem in a fairly generic way that should allow + // someone to use them as the basis for fuller services within core. + // + // The problem they solve is that of 'classified renditions' whereby the classification + // metadata on a node should appear to be on its renditions as well. This particular problem + // is simplified by the fact that renditions are not 'normal' nodes, as they are usually + // not accessed directly. This implementation also relies on the fact that RM already + // has interceptors for checking content classification and we can programmatically add + // the calls to metadata referral within the ContentClassificationService. + // + // To solve the problem of Metadata Referral in a general way would require the provision + // of 'MetadataReferral' interceptors that could sit in front of the NodeService. Only in this + // way could the services be used declaratively, thus minimising their impact on calling code. + // To add these to core would require careful assessment of their impact, not least in + // performance terms. This work is beyond RM's scope at this stage. + // The addition of such interceptors to the NodeService would also ensure that any metadata + // returned to e.g. Share for a particular node could automatically include 'linked' metadata + // which would be important. + // + // There are further enhancements that should be considered if these were developed into + // fuller services including the automatic registration of behaviours (onAddAspect, onRemoveAspect) + // for the aspect types which are linked. Currently these behaviours need to be hand-coded. + // See ClassifiedAspect.java for an example. + private ReferralRegistry registry; private NodeService nodeService; From 3efb64f11b7ac504086cbbacdb84970389a23e99 Mon Sep 17 00:00:00 2001 From: Neil McErlean Date: Wed, 9 Sep 2015 09:26:02 +0000 Subject: [PATCH 10/12] Slight refactor. ReferredMetadataService uses the registry to look up Referrals rather than the AdminService. Seems neater. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/BRANCHES/classified_renditions@111772 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../metadata-referral-context.xml | 1 + .../referredmetadata/ReferralRegistry.java | 24 ++++++++++++++++--- .../ReferredMetadataServiceImpl.java | 10 ++++++-- .../ReferredMetadataServiceImplUnitTest.java | 3 ++- 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/metadata-referral-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/metadata-referral-context.xml index f5c7c81edb..c9f97fdfe8 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/metadata-referral-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/metadata-referral-context.xml @@ -64,6 +64,7 @@ + diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralRegistry.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralRegistry.java index 2dacba8d45..653e2e7790 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralRegistry.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralRegistry.java @@ -79,11 +79,29 @@ public class ReferralRegistry */ public MetadataReferral getReferralForAssociation(QName assocType) { - for (MetadataReferral d : metadataReferrals) + for (MetadataReferral mr : metadataReferrals) { - if (d.getAssocType().equals(assocType)) + if (mr.getAssocType().equals(assocType)) { - return d; + return mr; + } + } + return null; + } + + /** + * Gets the {@link MetadataReferral} which is defined to handle the specified aspect. + * + * @param aspectName the name of the aspect whose {@link MetadataReferral} is sought. + * @return the {@link MetadataReferral} handling the specified aspect if there is one, else {@code null}. + */ + public MetadataReferral getReferralForAspect(QName aspectName) + { + for (MetadataReferral mr : metadataReferrals) + { + if (mr.getAspects().contains(aspectName)) + { + return mr; } } return null; diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferredMetadataServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferredMetadataServiceImpl.java index add2c367c2..fe9eaca755 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferredMetadataServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferredMetadataServiceImpl.java @@ -46,6 +46,7 @@ import java.util.Set; public class ReferredMetadataServiceImpl implements ReferredMetadataService { private ReferralAdminService referralAdminService; + private ReferralRegistry referralRegistry; private DictionaryService dictionaryService; private NodeService nodeService; @@ -54,6 +55,11 @@ public class ReferredMetadataServiceImpl implements ReferredMetadataService this.referralAdminService = service; } + public void setReferralRegistry(ReferralRegistry registry) + { + this.referralRegistry = registry; + } + public void setDictionaryService(DictionaryService service) { this.dictionaryService = service; @@ -71,7 +77,7 @@ public class ReferredMetadataServiceImpl implements ReferredMetadataService throw new InvalidNodeRefException(potentialReferrer); } - final MetadataReferral metadataReferral = referralAdminService.getReferralFor(aspectName); + final MetadataReferral metadataReferral = referralRegistry.getReferralForAspect(aspectName); if (metadataReferral == null) { @@ -91,7 +97,7 @@ public class ReferredMetadataServiceImpl implements ReferredMetadataService throw new InvalidNodeRefException(referrer); } - final MetadataReferral d = referralAdminService.getReferralFor(aspectName); + final MetadataReferral d = referralRegistry.getReferralForAspect(aspectName); if (d == null) { diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferredMetadataServiceImplUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferredMetadataServiceImplUnitTest.java index e04a96d087..0a818176ce 100644 --- a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferredMetadataServiceImplUnitTest.java +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferredMetadataServiceImplUnitTest.java @@ -64,6 +64,7 @@ public class ReferredMetadataServiceImplUnitTest @Mock DictionaryService mockDictionaryService; @Mock NodeService mockNodeService; @Mock ReferralAdminServiceImpl mockReferralAdminService; + @Mock ReferralRegistry mockReferralRegistry; /** This node has a referent node. */ private final NodeRef referringNode = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "referringNode"); @@ -116,7 +117,7 @@ public class ReferredMetadataServiceImplUnitTest when(mockReferralAdminService.getAttachedReferralsFrom(referringNode)).thenReturn(asSet(referral)); for (QName referredAspect : asSet(referredAspect1, referredAspect2)) { - when(mockReferralAdminService.getReferralFor(referredAspect)).thenReturn(referral); + when(mockReferralRegistry.getReferralForAspect(referredAspect)).thenReturn(referral); when(mockNodeService.hasAspect(referentNode, referredAspect)).thenReturn(true); } when(mockNodeService.getSourceAssocs(referentNode, referralAssocType)).thenReturn(asList(attachedReferralAssocRef)); From aeafa9fbbd2b194490762f4287ec1c630a66c24f Mon Sep 17 00:00:00 2001 From: Neil McErlean Date: Wed, 9 Sep 2015 10:42:29 +0000 Subject: [PATCH 11/12] Addressing review comment - don't have assoc types in the service API - have aspect names instead. I agree with this comment. I think assoc types are an implementation detail of this service. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/BRANCHES/classified_renditions@111779 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../metadata-referral-context.xml | 3 +- .../model/clf/ClassifiedRenditions.java | 2 +- .../model/clf/aspect/ClassifiedAspect.java | 4 +- .../ReferralAdminService.java | 34 ++++++--------- .../ReferralAdminServiceImpl.java | 41 +++++++------------ .../ReferredMetadataService.java | 4 +- .../clf/aspect/ClassifiedAspectUnitTest.java | 2 +- .../ReferralAdminServiceImplUnitTest.java | 25 +++++------ 8 files changed, 46 insertions(+), 69 deletions(-) diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/metadata-referral-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/metadata-referral-context.xml index c9f97fdfe8..02c35da73a 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/metadata-referral-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/metadata-referral-context.xml @@ -50,9 +50,8 @@ org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferralAdminService.attachReferrer=ACL_ALLOW org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferralAdminService.detachReferrer=ACL_ALLOW - org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferralAdminService.getDefinedReferrals=ACL_ALLOW - org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferralAdminService.getReferralFor=ACL_ALLOW org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferralAdminService.getAttachedReferralsFrom=ACL_ALLOW + org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferralAdminService.getAttachedReferralFrom=ACL_ALLOW org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferralAdminService.*=ACL_DENY diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/clf/ClassifiedRenditions.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/clf/ClassifiedRenditions.java index 1f92b82cb0..aec6c61e4b 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/clf/ClassifiedRenditions.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/clf/ClassifiedRenditions.java @@ -85,7 +85,7 @@ public class ClassifiedRenditions extends BaseBehaviourBean if (contentClassificationService.isClassified(sourceNode) && referralAdminService.getAttachedReferralFrom(renditionNodeRef, ASPECT_CLASSIFIED) != null) { - referralAdminService.attachReferrer(renditionNodeRef, sourceNode, ASSOC_CLASSIFIED_RENDITION); + referralAdminService.attachReferrer(renditionNodeRef, sourceNode, ASPECT_CLASSIFIED); } return null; diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/clf/aspect/ClassifiedAspect.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/clf/aspect/ClassifiedAspect.java index 7730f82875..622e345eb3 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/clf/aspect/ClassifiedAspect.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/clf/aspect/ClassifiedAspect.java @@ -148,9 +148,9 @@ public class ClassifiedAspect extends BaseBehaviourBean implements NodeServicePo for (ChildAssociationRef chAssRef : renditions) { final NodeRef renditionNode = chAssRef.getChildRef(); - if (referralAdminService.getAttachedReferralFrom(renditionNode, ASPECT_CLASSIFIED_RENDITION) == null) + if (referralAdminService.getAttachedReferralFrom(renditionNode, ASPECT_CLASSIFIED) == null) { - referralAdminService.attachReferrer(renditionNode, classifiedNode, ASSOC_CLASSIFIED_RENDITION); + referralAdminService.attachReferrer(renditionNode, classifiedNode, ASPECT_CLASSIFIED); } } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralAdminService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralAdminService.java index cafad45b0e..52a94d7a09 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralAdminService.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralAdminService.java @@ -32,7 +32,8 @@ import java.util.Set; * Then any read request for relevant metadata such as hasAspect or getProperties can be delegated to the * linked node. *

- * For a link to be made, there must be a {@link #getDefinedReferrals() defined MetadataReferral} already in the system. + * For a link to be made, there must be a {@link ReferralRegistry#getMetadataReferrals()} defined MetadataReferral} + * already in the system. * This means that a peer-association type will have to have been declared and that a spring bean will have to have * defined which aspects are to be handled by this {@link MetadataReferral}. * @@ -45,41 +46,30 @@ public interface ReferralAdminService * Creates a link between two nodes such that the first {@code referrer} can 'inherit' or reuse some aspect * metadata from another node - the {@code referrer}. *

+ * Note that attaching a referrer for the specified aspect will also link the two nodes for + * all aspects defined in the {@link MetadataReferral}. + *

* Note that links can currently only extend between two pairs of nodes and cannot be chained. * * @param referrer the node which is to inherit additional metadata. * @param referent the node which will provide the additional metadata. - * @param assocType the type of the peer association which will link the two nodes. + * @param aspectName the name of the aspect whose metadata is to be attached. * @return a {@link MetadataReferral} object which defines the link type. * @throws ChainedMetadataReferralUnsupported if an attempt is made to attach nodes such that a chain would be made. */ - // FIXME Remove assocType entirely from the API - MetadataReferral attachReferrer(NodeRef referrer, NodeRef referent, QName assocType); + MetadataReferral attachReferrer(NodeRef referrer, NodeRef referent, QName aspectName); /** * Removes an existing metadata link between two nodes. + *

+ * Note that detaching a referrer for the specified aspect will also unlink the two nodes for + * all aspects defined in the {@link MetadataReferral}. * * @param referrer the node which has been linked to a metadata source. - * @param assocType the type of the peer association forming the link. + * @param aspectName the name of the aspect whose metadata is to be detached. * @return the removed {@link MetadataReferral}. */ - MetadataReferral detachReferrer(NodeRef referrer, QName assocType); - - /** - * Gets the set of defined {@link MetadataReferral}s. - * - * @return the set of defined {@link MetadataReferral}. - */ - Set getDefinedReferrals(); - - /** - * Gets the {@link MetadataReferral} which contains the specified {@code aspectName} if there is one. - * Note that only one {@link MetadataReferral} may declare that it handles any particular aspect. - * - * @param aspectName the name of the aspect whose {@link MetadataReferral} is sought. - * @return the {@link MetadataReferral} which handles the specified aspect, if there is one. - */ - MetadataReferral getReferralFor(QName aspectName); + MetadataReferral detachReferrer(NodeRef referrer, QName aspectName); // FIXME Chase all references /** * Gets the set of {@link MetadataReferral}s which are currently applied from the specified {@code referrer}. diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralAdminServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralAdminServiceImpl.java index f2b43b1dc9..4aa8c4afbb 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralAdminServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralAdminServiceImpl.java @@ -79,9 +79,15 @@ public class ReferralAdminServiceImpl implements ReferralAdminService this.registry = registry; } - @Override public MetadataReferral attachReferrer(NodeRef referrer, NodeRef referent, QName assocType) + @Override public MetadataReferral attachReferrer(NodeRef referrer, NodeRef referent, QName aspectName) { - final MetadataReferral metadataReferral = getReferralForAssociation(assocType); + final MetadataReferral metadataReferral = registry.getReferralForAspect(aspectName); + if (metadataReferral == null) + { + throw new IllegalArgumentException("No defined " + MetadataReferral.class.getSimpleName() + + " for aspect " + aspectName); + } + final QName assocType = metadataReferral.getAssocType(); // Prevent the creation of chains of metadata linking from node A to B to C. @@ -135,8 +141,11 @@ public class ReferralAdminServiceImpl implements ReferralAdminService return metadataReferral; } - @Override public MetadataReferral detachReferrer(NodeRef referrer, QName assocType) + @Override public MetadataReferral detachReferrer(NodeRef referrer, QName aspectName) { + final MetadataReferral referral = registry.getReferralForAspect(aspectName); + final QName assocType = referral.getAssocType(); + // Is the association there? final List assocs = nodeService.getTargetAssocs(referrer, assocType); @@ -146,36 +155,19 @@ public class ReferralAdminServiceImpl implements ReferralAdminService } else { - MetadataReferral result = getReferralForAssociation(assocType); - // There should only be one such association... but we'll remove them all just in case for (AssociationRef assocRef : assocs) { nodeService.removeAssociation(referrer, assocRef.getTargetRef(), assocType); } - return result; + return referral; } } - @Override public MetadataReferral getReferralFor(QName aspectName) - { - MetadataReferral metadataReferral = null; - - for (MetadataReferral d : getDefinedReferrals()) - { - if (d.getAspects().contains(aspectName)) - { - metadataReferral = d; - break; - } - } - return metadataReferral; - } - @Override public Set getAttachedReferralsFrom(NodeRef referrer) { - final Set allMetadataReferrals = getDefinedReferrals(); + final Set allMetadataReferrals = registry.getMetadataReferrals(); final Set result = new HashSet<>(); for (MetadataReferral d : allMetadataReferrals) @@ -200,9 +192,4 @@ public class ReferralAdminServiceImpl implements ReferralAdminService } return null; } - - @Override public Set getDefinedReferrals() - { - return registry.getMetadataReferrals(); - } } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferredMetadataService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferredMetadataService.java index 8e18c2bcf7..77d234dbfe 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferredMetadataService.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferredMetadataService.java @@ -29,8 +29,8 @@ import java.io.Serializable; import java.util.Map; /** - * This service provides read-only access to linked metadata. - * TODO complete! Include definition of referent etc or link to package.html + * This service provides read-only access to linked metadata. It is primarily concerned with data transfer. + * For an overview, see the package javadoc. * * @author Neil Mc Erlean * @since 2.4.a diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/model/clf/aspect/ClassifiedAspectUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/model/clf/aspect/ClassifiedAspectUnitTest.java index 30e89eff75..0d2729b503 100644 --- a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/model/clf/aspect/ClassifiedAspectUnitTest.java +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/model/clf/aspect/ClassifiedAspectUnitTest.java @@ -142,7 +142,7 @@ public class ClassifiedAspectUnitTest implements ClassifiedContentModel for (NodeRef rendition : asList(RENDITION_1, RENDITION_2)) { - verify(mockReferralAdminService).attachReferrer(rendition, NODE_REF, ASSOC_CLASSIFIED_RENDITION); + verify(mockReferralAdminService).attachReferrer(rendition, NODE_REF, ASPECT_CLASSIFIED); } } diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralAdminServiceImplUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralAdminServiceImplUnitTest.java index 6e31f011df..f5388c9f06 100644 --- a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralAdminServiceImplUnitTest.java +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/referredmetadata/ReferralAdminServiceImplUnitTest.java @@ -84,17 +84,17 @@ public class ReferralAdminServiceImplUnitTest } @Test(expected=IllegalArgumentException.class) - public void attachingReferrerWithNoAssociationConfiguredShouldFail() + public void attachingReferrerWithNoAspectConfiguredShouldFail() { - referralAdminService.attachReferrer(node2, node1, assoc1); + referralAdminService.attachReferrer(node2, node1, aspect1); } @Test public void attachDetach() { - when(mockRegistry.getReferralForAssociation(assoc1)).thenReturn(referral1); + when(mockRegistry.getReferralForAspect(aspect1)).thenReturn(referral1); // attach - MetadataReferral d = attachReferrer(node1, node2, assoc1); + MetadataReferral d = attachReferrer(node1, node2, aspect1); // validate assertEquals(assoc1, d.getAssocType()); @@ -103,24 +103,25 @@ public class ReferralAdminServiceImplUnitTest assertFalse(mockReferredMetadataService.isReferringMetadata(node1, aspect3)); // detach - assertEquals(d, referralAdminService.detachReferrer(node1, assoc1)); + assertEquals(d, referralAdminService.detachReferrer(node1, aspect1)); } - private MetadataReferral attachReferrer(NodeRef referrer, NodeRef referent, QName assocType) + private MetadataReferral attachReferrer(NodeRef referrer, NodeRef referent, QName aspectName) { - MetadataReferral d = referralAdminService.attachReferrer(referrer, referent, assocType); + MetadataReferral mr = referralAdminService.attachReferrer(referrer, referent, aspectName); + final QName assocType = mr.getAssocType(); when(mockNodeService.getSourceAssocs(referent, assocType)).thenReturn(asList(new AssociationRef(referrer, assocType, referent))); when(mockNodeService.getTargetAssocs(referrer, assocType)).thenReturn(asList(new AssociationRef(referrer, assocType, referent))); - for (QName aspect : d.getAspects()) + for (QName aspect : mr.getAspects()) { when(mockReferredMetadataService.isReferringMetadata(referrer, aspect)).thenReturn(true); } - return d; + return mr; } @Test public void chainsOfDelegationShouldBePrevented() { - when(mockRegistry.getReferralForAssociation(assoc1)).thenReturn(referral1); + when(mockRegistry.getReferralForAspect(aspect1)).thenReturn(referral1); // The node already has a delegation in place: node1 -> node2. We're trying to add to the // end of the chain: node2 -> node3 @@ -128,13 +129,13 @@ public class ReferralAdminServiceImplUnitTest when(mockNodeService.getTargetAssocs(node1, assoc1)).thenReturn(asList(new AssociationRef(node1, assoc1, node2))); expectedException(ChainedMetadataReferralUnsupported.class, () -> { - referralAdminService.attachReferrer(node2, node3, assoc1); + referralAdminService.attachReferrer(node2, node3, aspect1); return null; }); // Now try to add to the start of the chain: node3 -> node1 expectedException(ChainedMetadataReferralUnsupported.class, () -> { - referralAdminService.attachReferrer(node3, node1, assoc1); + referralAdminService.attachReferrer(node3, node1, aspect1); return null; }); } From 4a9eb2c75d2afd60f9c86dbb83e7bf28f71afb89 Mon Sep 17 00:00:00 2001 From: Neil McErlean Date: Thu, 10 Sep 2015 13:03:45 +0000 Subject: [PATCH 12/12] Applying code review comment. I added an 'mr' prefix to the spring beans, which we hope will make our lives easier if this Metadata Referral stuff ever makes it into core. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/BRANCHES/classified_renditions@111855 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../classified-content-context.xml | 8 ++-- .../metadata-referral-context.xml | 40 +++++++++---------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/classified-content-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/classified-content-context.xml index f71c08977a..a26fae24c0 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/classified-content-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/classified-content-context.xml @@ -125,7 +125,7 @@ - + @@ -195,7 +195,7 @@ - + @@ -243,7 +243,7 @@ class="org.alfresco.module.org_alfresco_module_rm.model.clf.aspect.ClassifiedAspect" parent="rm.baseBehaviour"> - + @@ -251,7 +251,7 @@ class="org.alfresco.module.org_alfresco_module_rm.model.clf.ClassifiedRenditions" parent="rm.baseBehaviour"> - + diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/metadata-referral-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/metadata-referral-context.xml index 02c35da73a..11cadeabbb 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/metadata-referral-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/metadata-referral-context.xml @@ -12,29 +12,29 @@ http://www.springframework.org/schema/aop/spring-aop-3.0.xsd "> - - + - + org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferralAdminService - + - + - + - + @@ -45,7 +45,7 @@ - + org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferralAdminService.attachReferrer=ACL_ALLOW @@ -60,31 +60,31 @@ - - - + + - + org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferredMetadataService - + - + - + - + @@ -95,7 +95,7 @@ - + org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferredMetadataService.isReferringMetadata=ACL_ALLOW @@ -109,15 +109,15 @@ - - - +