This checkin provides the non-RM-specific parts of metadata delegation, which are required for the refactor of classified renditions needed for RM-2549.

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
This commit is contained in:
Neil McErlean
2015-09-02 12:03:38 +00:00
parent 06a7ef9b73
commit 9e4ccdd053
13 changed files with 1500 additions and 1 deletions

View File

@@ -0,0 +1,123 @@
<?xml version='1.0' encoding='UTF-8'?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<!-- Metadata Delegation Service -->
<bean id="delegationAdminService"
class="org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationAdminServiceImpl" >
<property name="delegationRegistry" ref="delegationRegistry" />
<property name="nodeService" ref="NodeService" />
</bean>
<bean id="DelegationAdminService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationAdminService</value>
</property>
<property name="target">
<ref bean="delegationAdminService"/>
</property>
<property name="interceptorNames">
<list>
<idref local="DelegationAdminService_transaction"/>
<idref bean="exceptionTranslator"/>
<idref local="DelegationAdminService_security"/>
</list>
</property>
</bean>
<bean id="DelegationAdminService_transaction" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager">
<ref bean="transactionManager"/>
</property>
<property name="transactionAttributes">
<props>
<prop key="*">${server.transaction.mode.default}</prop>
</props>
</property>
</bean>
<bean id="DelegationAdminService_security" parent="baseSecurity">
<property name="objectDefinitionSource">
<value>
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
</value>
</property>
</bean>
<!-- Metadata Delegation Service -->
<bean id="delegationService"
class="org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationServiceImpl" >
<property name="delegationAdminService" ref="DelegationAdminService" />
<property name="dictionaryService" ref="dictionaryService" />
<property name="nodeService" ref="NodeService" />
</bean>
<bean id="DelegationService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationService</value>
</property>
<property name="target">
<ref bean="delegationService"/>
</property>
<property name="interceptorNames">
<list>
<idref local="DelegationService_transaction"/>
<idref bean="exceptionTranslator"/>
<idref local="DelegationService_security"/>
</list>
</property>
</bean>
<bean id="DelegationService_transaction" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager">
<ref bean="transactionManager"/>
</property>
<property name="transactionAttributes">
<props>
<prop key="*">${server.transaction.mode.default}</prop>
</props>
</property>
</bean>
<bean id="DelegationService_security" parent="baseSecurity">
<property name="objectDefinitionSource">
<value>
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
</value>
</property>
</bean>
<bean name="delegationRegistry"
class="org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationRegistry">
</bean>
<bean name="baseDelegateAssoc"
abstract="true"
class="org.alfresco.module.org_alfresco_module_rm.metadatadelegation.Delegation"
init-method="validateAndRegister">
<property name="delegationRegistry" ref="delegationRegistry" />
<property name="dictionaryService" ref="dictionaryService" />
</bean>
</beans>

View File

@@ -267,9 +267,12 @@
<!-- Import the RM webscript's -->
<import resource="classpath:alfresco/module/org_alfresco_module_rm/rm-webscript-context.xml"/>
<!-- Import the Metadata Delegation Services -->
<import resource="classpath:alfresco/module/org_alfresco_module_rm/metadata-delegation-context.xml"/>
<!-- Import the Classified Content Services -->
<import resource="classpath:alfresco/module/org_alfresco_module_rm/classified-content-context.xml"/>
<!-- Import the Content Services -->
<import resource="classpath:alfresco/module/org_alfresco_module_rm/content-context.xml"/>

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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.
* <p/>
* The connection between the nodes is made with a specified {@link #assocType peer association}.
*<p/>
* 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<QName> 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<QName> 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<QName> 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();
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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.
* <p/>
* 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}.
* <p/>
* 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<Delegation> 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<Delegation> getDelegationsFrom(NodeRef nodeRef);
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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<AssociationRef> existingDelegatorAssocs = nodeService.getSourceAssocs(nodeRef, assocType);
if ( !existingDelegatorAssocs.isEmpty())
{
final List<NodeRef> existingDelegators = transform(existingDelegatorAssocs,
new Function<AssociationRef, NodeRef>()
{
@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<AssociationRef> 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<NodeRef> existingDelegates = transform(existingDelegateAssocs,
new Function<AssociationRef, NodeRef>()
{
@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<AssociationRef> 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<Delegation> getDelegationsFrom(NodeRef nodeRef)
{
final Set<Delegation> allDelegations = getDefinedDelegations();
final Set<Delegation> 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<Delegation> getDefinedDelegations()
{
return registry.getDelegations();
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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<NodeRef> nodesAlreadyDelegating;
public ChainedDelegationUnsupported(String msgId, List<NodeRef> nodesAlreadyDelegating)
{
super(msgId);
this.nodesAlreadyDelegating = nodesAlreadyDelegating;
}
public List<NodeRef> 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();
}
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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<Delegation> 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<Delegation> getDelegations()
{
return Collections.unmodifiableSet(delegations);
}
public Delegation getDelegateForAssociation(QName assocType)
{
for (Delegation d : delegations)
{
if (d.getAssocType().equals(assocType))
{
return d;
}
}
return null;
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright (C) 2005-2015 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.module.org_alfresco_module_rm.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<QName, Serializable> 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<Delegation, NodeRef> getDelegations(NodeRef nodeRef);
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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<AssociationRef> 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<AssociationRef> assocs = nodeService.getTargetAssocs(nodeRef, assocType);
return assocs.isEmpty() ? null : assocs.get(0).getTargetRef();
}
}
@Override public Map<QName, Serializable> 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<QName, Serializable> allProps = nodeService.getProperties(delegateNode);
Map<QName, Serializable> aspectProps = filterKeys(allProps,
new Function<QName, Boolean>()
{
@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<QName, Serializable> 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<Delegation, NodeRef> getDelegations(NodeRef nodeRef)
{
Set<Delegation> delegations = delegationAdminService.getDelegationsFrom(nodeRef);
Map<Delegation, NodeRef> 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;
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
/**
* This package contains the types that deliver the Metadata Delegation feature.
* Metadata delegation allows read-only <em>aspect</em> metadata for any given Alfresco node to
* be sourced from another node, the delegate.
* <p/>
* 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.
* <p/>
* Multiple nodes may share the same delegate node and one node may be linked to multiple
* delegates.
* <p/>
* 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.
* <p/>
* See {@link org.alfresco.module.org_alfresco_module_rm.metadatadelegation.DelegationAdminService}
* for details on how to create and destroy delegation links between nodes.
* <p/>
* 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.
* <p/>
* 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;

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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;
});
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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<QName, Serializable>()
{{
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<QName, Serializable> 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<Delegation, NodeRef> expectedDelegations = new HashMap<>();
expectedDelegations.put(delegate, delegateNode);
assertEquals(expectedDelegations, delegationService.getDelegations(nodeWithDelegate));
assertEquals(emptyMap(), delegationService.getDelegations(nodeWithoutDelegate));
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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<Delegation> 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();
}
}