mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
Merge of classified_renditions branch which introduces MetadataReferralService and fixes RM-2549.
============================================================================== Merged to : 111287: 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<T> rather than T. 111292: This checkin provides the non-RM-specific parts of metadata delegation, which are required for the refactor of classified renditions needed for RM-2549. 111633: Massive renaming. Delegate/Delegation becomes Referrer, Referent and things *do* make a little more sense. 111643: This is the RM-specific parts of the refactor for classified renditions - see RM-2549. 111696: Addressing code review comments. 111703: Addressing code review comments 111707: Addressing review comments. Clearer use of lambdas due to default methods in java.util.Collection. 111768: 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. 111772: Slight refactor. ReferredMetadataService uses the registry to look up Referrals rather than the AdminService. Seems neater. 111779: 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. 111855: 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/HEAD@111864 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -58,7 +58,7 @@
|
||||
<property name="exemptionCategoryConfigLocation" value="${rm.classification.exemptionCategoriesFile}" />
|
||||
</bean>
|
||||
|
||||
<!-- Classification Scheme Service -->
|
||||
<!-- Classification Scheme Service -->Cl
|
||||
|
||||
<bean id="classificationSchemeService"
|
||||
class="org.alfresco.module.org_alfresco_module_rm.classification.ClassificationSchemeServiceImpl"
|
||||
@@ -125,6 +125,14 @@
|
||||
<constructor-arg ref="classificationServiceDAO"/>
|
||||
</bean>
|
||||
|
||||
<bean name="classifiedRenditionAssoc" parent="mr.baseReferralAssoc">
|
||||
<property name="assocType" value="clf:classifiedRendition" />
|
||||
<property name="aspects">
|
||||
<set>
|
||||
<value>clf:classified</value>
|
||||
</set>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<!-- Security Clearance Service -->
|
||||
|
||||
@@ -187,6 +195,7 @@
|
||||
<property name="securityClearanceService" ref="SecurityClearanceService"/>
|
||||
<property name="classificationServiceBootstrap" ref="classificationServiceBootstrap"/>
|
||||
<property name="freezeService" ref="FreezeService"/>
|
||||
<property name="referredMetadataService" ref="mr.ReferredMetadataService" />
|
||||
</bean>
|
||||
|
||||
<bean id="ContentClassificationService" class="org.springframework.aop.framework.ProxyFactoryBean">
|
||||
@@ -234,15 +243,15 @@
|
||||
class="org.alfresco.module.org_alfresco_module_rm.model.clf.aspect.ClassifiedAspect"
|
||||
parent="rm.baseBehaviour">
|
||||
<property name="classificationSchemeService" ref="ClassificationSchemeService" />
|
||||
<property name="referralAdminService" ref="mr.ReferralAdminService" />
|
||||
<property name="renditionService" ref="RenditionService" />
|
||||
<property name="coreServicesExtras" ref="rm.coreServicesExtras" />
|
||||
</bean>
|
||||
|
||||
<bean id="clf.classifiedRenditions"
|
||||
class="org.alfresco.module.org_alfresco_module_rm.model.clf.ClassifiedRenditions"
|
||||
parent="rm.baseBehaviour">
|
||||
<property name="contentClassificationService" ref="ContentClassificationService" />
|
||||
<property name="referralAdminService" ref="mr.ReferralAdminService" />
|
||||
<property name="renditionService" ref="RenditionService" />
|
||||
<property name="coreServicesExtras" ref="rm.coreServicesExtras" />
|
||||
</bean>
|
||||
</beans>
|
||||
|
@@ -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 Referral Admin Service -->
|
||||
<bean id="mr.referralAdminService"
|
||||
class="org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferralAdminServiceImpl" >
|
||||
<property name="referralRegistry" ref="mr.referralRegistry" />
|
||||
<property name="nodeService" ref="NodeService" />
|
||||
</bean>
|
||||
|
||||
<bean id="mr.ReferralAdminService" class="org.springframework.aop.framework.ProxyFactoryBean">
|
||||
<property name="proxyInterfaces">
|
||||
<value>org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferralAdminService</value>
|
||||
</property>
|
||||
<property name="target">
|
||||
<ref bean="mr.referralAdminService"/>
|
||||
</property>
|
||||
<property name="interceptorNames">
|
||||
<list>
|
||||
<idref local="mr.ReferralAdminService_transaction"/>
|
||||
<idref bean="exceptionTranslator"/>
|
||||
<idref local="mr.ReferralAdminService_security"/>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="mr.ReferralAdminService_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="mr.ReferralAdminService_security" parent="baseSecurity">
|
||||
<property name="objectDefinitionSource">
|
||||
<value>
|
||||
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.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
|
||||
</value>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
|
||||
|
||||
<!-- Referred Metadata Service -->
|
||||
<bean id="mr.referredMetadataService"
|
||||
class="org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferredMetadataServiceImpl" >
|
||||
<property name="referralAdminService" ref="mr.ReferralAdminService" />
|
||||
<property name="referralRegistry" ref="mr.referralRegistry" />
|
||||
<property name="dictionaryService" ref="dictionaryService" />
|
||||
<property name="nodeService" ref="NodeService" />
|
||||
</bean>
|
||||
|
||||
<bean id="mr.ReferredMetadataService" class="org.springframework.aop.framework.ProxyFactoryBean">
|
||||
<property name="proxyInterfaces">
|
||||
<value>org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferredMetadataService</value>
|
||||
</property>
|
||||
<property name="target">
|
||||
<ref bean="mr.referredMetadataService"/>
|
||||
</property>
|
||||
<property name="interceptorNames">
|
||||
<list>
|
||||
<idref local="mr.ReferredMetadataService_transaction"/>
|
||||
<idref bean="exceptionTranslator"/>
|
||||
<idref local="mr.ReferredMetadataService_security"/>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="mr.ReferredMetadataService_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="mr.ReferredMetadataService_security" parent="baseSecurity">
|
||||
<property name="objectDefinitionSource">
|
||||
<value>
|
||||
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
|
||||
</value>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean name="mr.referralRegistry"
|
||||
class="org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferralRegistry">
|
||||
</bean>
|
||||
|
||||
<bean name="mr.baseReferralAssoc"
|
||||
abstract="true"
|
||||
class="org.alfresco.module.org_alfresco_module_rm.referredmetadata.MetadataReferral"
|
||||
init-method="validateAndRegister">
|
||||
<property name="referralRegistry" ref="mr.referralRegistry" />
|
||||
<property name="dictionaryService" ref="dictionaryService" />
|
||||
</bean>
|
||||
</beans>
|
@@ -38,11 +38,6 @@
|
||||
type="org.alfresco.module.org_alfresco_module_rm.classification.ReclassificationValueConstraint" />
|
||||
</constraints>
|
||||
|
||||
<!-- Types -->
|
||||
<types>
|
||||
|
||||
</types>
|
||||
|
||||
<!-- Aspects -->
|
||||
<aspects>
|
||||
<!-- Classified aspect -->
|
||||
@@ -178,5 +173,21 @@
|
||||
</property>
|
||||
</properties>
|
||||
</aspect>
|
||||
|
||||
<aspect name="clf:classifiedRendition">
|
||||
<associations>
|
||||
<association name="clf:classifiedRendition">
|
||||
<source>
|
||||
<mandatory>true</mandatory>
|
||||
<many>true</many>
|
||||
</source>
|
||||
<target>
|
||||
<class>cm:content</class>
|
||||
<mandatory>true</mandatory>
|
||||
<many>true</many>
|
||||
</target>
|
||||
</association>
|
||||
</associations>
|
||||
</aspect>
|
||||
</aspects>
|
||||
</model>
|
@@ -267,6 +267,9 @@
|
||||
<!-- Import the RM webscript's -->
|
||||
<import resource="classpath:alfresco/module/org_alfresco_module_rm/rm-webscript-context.xml"/>
|
||||
|
||||
<!-- Import the Metadata Referral Services -->
|
||||
<import resource="classpath:alfresco/module/org_alfresco_module_rm/metadata-referral-context.xml"/>
|
||||
|
||||
<!-- Import the Classified Content Services -->
|
||||
<import resource="classpath:alfresco/module/org_alfresco_module_rm/classified-content-context.xml"/>
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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");
|
||||
}
|
@@ -20,14 +20,15 @@ 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;
|
||||
|
||||
@@ -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<Void>()
|
||||
{
|
||||
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, ASPECT_CLASSIFIED);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}, authenticationUtil.getSystemUserName());
|
||||
|
@@ -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,6 +40,11 @@ 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
|
||||
*
|
||||
@@ -55,34 +56,31 @@ import org.alfresco.service.namespace.QName;
|
||||
)
|
||||
public class ClassifiedAspect extends BaseBehaviourBean implements NodeServicePolicies.OnUpdatePropertiesPolicy,
|
||||
NodeServicePolicies.OnAddAspectPolicy,
|
||||
NodeServicePolicies.OnRemoveAspectPolicy,
|
||||
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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* Validates the consistency of the properties.
|
||||
*/
|
||||
@Override
|
||||
@@ -91,7 +89,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<QName, Serializable> before,
|
||||
final Map<QName, Serializable> after)
|
||||
{
|
||||
@@ -101,7 +99,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 +111,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;
|
||||
}
|
||||
@@ -128,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.
|
||||
* <p>
|
||||
* Validates the consistency of the properties.
|
||||
*/
|
||||
@@ -136,31 +132,62 @@ 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<Void>()
|
||||
{
|
||||
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<ChildAssociationRef> renditions = renditionService.getRenditions(classifiedNode);
|
||||
for (ChildAssociationRef chAssRef : renditions)
|
||||
{
|
||||
final NodeRef renditionNode = chAssRef.getChildRef();
|
||||
if (referralAdminService.getAttachedReferralFrom(renditionNode, ASPECT_CLASSIFIED) == null)
|
||||
{
|
||||
referralAdminService.attachReferrer(renditionNode, classifiedNode, ASPECT_CLASSIFIED);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}, AuthenticationUtil.getSystemUserName());
|
||||
}
|
||||
|
||||
private void copyClassifiedPropertiesToRenditions(NodeRef nodeRef)
|
||||
/**
|
||||
* Behaviour associated with removing the classified aspect.
|
||||
* <p>
|
||||
* Validates the consistency of the properties.
|
||||
*/
|
||||
@Override
|
||||
@Behaviour
|
||||
(
|
||||
kind = BehaviourKind.CLASS,
|
||||
notificationFrequency = NotificationFrequency.FIRST_EVENT
|
||||
)
|
||||
public void onRemoveAspect(final NodeRef classifiedNode, final QName aspectTypeQName)
|
||||
{
|
||||
// All renditions should be given the same classification as their source node
|
||||
for (final ChildAssociationRef chAssRef : renditionService.getRenditions(nodeRef))
|
||||
AuthenticationUtil.runAs(new RunAsWork<Void>()
|
||||
{
|
||||
final NodeRef renditionNode = chAssRef.getChildRef();
|
||||
servicesExtras.copyAspect(nodeRef, renditionNode, ASPECT_CLASSIFIED);
|
||||
public Void doWork()
|
||||
{
|
||||
// If this node has any renditions, we should remove the metadata link
|
||||
final List<ChildAssociationRef> 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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2015 Alfresco Software Limited.
|
||||
*
|
||||
* This file is part of Alfresco
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.alfresco.module.org_alfresco_module_rm.referredmetadata;
|
||||
|
||||
import org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferredMetadataException.InvalidMetadataReferral;
|
||||
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 {@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.
|
||||
* <p/>
|
||||
* The connection between the nodes is made with a specified {@link #assocType peer association}.
|
||||
*<p/>
|
||||
* 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 2.4.a
|
||||
*/
|
||||
public class MetadataReferral
|
||||
{
|
||||
private DictionaryService dictionaryService;
|
||||
private ReferralRegistry referralRegistry;
|
||||
private Set<QName> aspects;
|
||||
private QName assocType;
|
||||
|
||||
public MetadataReferral()
|
||||
{
|
||||
// Intentionally empty.
|
||||
}
|
||||
|
||||
public void setDictionaryService(DictionaryService service)
|
||||
{
|
||||
this.dictionaryService = service;
|
||||
}
|
||||
|
||||
public void setReferralRegistry(ReferralRegistry registry)
|
||||
{
|
||||
this.referralRegistry = 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 InvalidMetadataReferral("Illegal null assocType");
|
||||
}
|
||||
if (aspects == null || aspects.isEmpty())
|
||||
{
|
||||
throw new InvalidMetadataReferral("Illegal null or empty aspects set");
|
||||
}
|
||||
if (dictionaryService.getAssociation(assocType) == null)
|
||||
{
|
||||
throw new InvalidMetadataReferral("Association not found: " + assocType);
|
||||
}
|
||||
for (QName aspect : aspects)
|
||||
{
|
||||
if (dictionaryService.getAspect(aspect) == null)
|
||||
{
|
||||
throw new InvalidMetadataReferral("Aspect not found: " + aspect);
|
||||
}
|
||||
}
|
||||
|
||||
this.referralRegistry.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 referred. */
|
||||
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 MetadataReferral)
|
||||
{
|
||||
MetadataReferral that = (MetadataReferral)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();
|
||||
}
|
||||
}
|
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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.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.
|
||||
* <p/>
|
||||
* 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}.
|
||||
*
|
||||
* @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}.
|
||||
* <p/>
|
||||
* Note that attaching a referrer for the specified aspect will also link the two nodes for
|
||||
* all aspects defined in the {@link MetadataReferral}.
|
||||
* <p/>
|
||||
* 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 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.
|
||||
*/
|
||||
MetadataReferral attachReferrer(NodeRef referrer, NodeRef referent, QName aspectName);
|
||||
|
||||
/**
|
||||
* Removes an existing metadata link between two nodes.
|
||||
* <p/>
|
||||
* 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 aspectName the name of the aspect whose metadata is to be detached.
|
||||
* @return the removed {@link MetadataReferral}.
|
||||
*/
|
||||
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}.
|
||||
* 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<MetadataReferral> 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);
|
||||
}
|
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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
|
||||
{
|
||||
// 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;
|
||||
|
||||
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 aspectName)
|
||||
{
|
||||
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.
|
||||
|
||||
// If any nodes are already linked to referrer for the specified assoc, then we can't chain.
|
||||
final List<AssociationRef> existingReferrerAssocs = nodeService.getSourceAssocs(referrer, assocType);
|
||||
if ( !existingReferrerAssocs.isEmpty())
|
||||
{
|
||||
final List<NodeRef> existingReferrers = transform(existingReferrerAssocs,
|
||||
new Function<AssociationRef, NodeRef>()
|
||||
{
|
||||
@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<AssociationRef> 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<NodeRef> existingReferents = transform(existingReferentAssocs,
|
||||
new Function<AssociationRef, NodeRef>()
|
||||
{
|
||||
@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 aspectName)
|
||||
{
|
||||
final MetadataReferral referral = registry.getReferralForAspect(aspectName);
|
||||
final QName assocType = referral.getAssocType();
|
||||
|
||||
// Is the association there?
|
||||
final List<AssociationRef> assocs = nodeService.getTargetAssocs(referrer, assocType);
|
||||
|
||||
if (assocs == null || assocs.isEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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 referral;
|
||||
}
|
||||
}
|
||||
|
||||
@Override public Set<MetadataReferral> getAttachedReferralsFrom(NodeRef referrer)
|
||||
{
|
||||
final Set<MetadataReferral> allMetadataReferrals = registry.getMetadataReferrals();
|
||||
|
||||
final Set<MetadataReferral> 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<MetadataReferral> allMetadataReferrals = getAttachedReferralsFrom(referrer);
|
||||
|
||||
for (MetadataReferral d : allMetadataReferrals)
|
||||
{
|
||||
if (d.getAspects().contains(aspectName)) return d;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<MetadataReferral> 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<MetadataReferral> 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 mr : metadataReferrals)
|
||||
{
|
||||
if (mr.getAssocType().equals(assocType))
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<NodeRef> existingReferrers;
|
||||
|
||||
public ChainedMetadataReferralUnsupported(String msgId, List<NodeRef> existingReferrers)
|
||||
{
|
||||
super(msgId);
|
||||
this.existingReferrers = existingReferrers;
|
||||
}
|
||||
|
||||
public List<NodeRef> 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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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. It is primarily concerned with data transfer.
|
||||
* For an overview, see the package javadoc.
|
||||
*
|
||||
* @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<QName, Serializable> 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<MetadataReferral, NodeRef> getAttachedReferrals(NodeRef potentialReferrer);
|
||||
}
|
||||
|
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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.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;
|
||||
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 2.4.a
|
||||
*/
|
||||
public class ReferredMetadataServiceImpl implements ReferredMetadataService
|
||||
{
|
||||
private ReferralAdminService referralAdminService;
|
||||
private ReferralRegistry referralRegistry;
|
||||
private DictionaryService dictionaryService;
|
||||
private NodeService nodeService;
|
||||
|
||||
public void setReferralAdminService(ReferralAdminService service)
|
||||
{
|
||||
this.referralAdminService = service;
|
||||
}
|
||||
|
||||
public void setReferralRegistry(ReferralRegistry registry)
|
||||
{
|
||||
this.referralRegistry = registry;
|
||||
}
|
||||
|
||||
public void setDictionaryService(DictionaryService service)
|
||||
{
|
||||
this.dictionaryService = service;
|
||||
}
|
||||
|
||||
public void setNodeService(NodeService service)
|
||||
{
|
||||
this.nodeService = service;
|
||||
}
|
||||
|
||||
@Override public boolean isReferringMetadata(NodeRef potentialReferrer, QName aspectName)
|
||||
{
|
||||
if ( !nodeService.exists(potentialReferrer))
|
||||
{
|
||||
throw new InvalidNodeRefException(potentialReferrer);
|
||||
}
|
||||
|
||||
final MetadataReferral metadataReferral = referralRegistry.getReferralForAspect(aspectName);
|
||||
|
||||
if (metadataReferral == null)
|
||||
{
|
||||
throw new MetadataReferralNotFound("No defined referral found for aspect: " + aspectName);
|
||||
}
|
||||
else
|
||||
{
|
||||
final List<AssociationRef> targetAssocs = nodeService.getTargetAssocs(potentialReferrer, metadataReferral.getAssocType());
|
||||
return !targetAssocs.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override public NodeRef getReferentNode(NodeRef referrer, QName aspectName)
|
||||
{
|
||||
if ( !nodeService.exists(referrer))
|
||||
{
|
||||
throw new InvalidNodeRefException(referrer);
|
||||
}
|
||||
|
||||
final MetadataReferral d = referralRegistry.getReferralForAspect(aspectName);
|
||||
|
||||
if (d == null)
|
||||
{
|
||||
throw new MetadataReferralNotFound("No defined referral found for aspect: " + aspectName);
|
||||
}
|
||||
else
|
||||
{
|
||||
final QName assocType = d.getAssocType();
|
||||
final List<AssociationRef> assocs = nodeService.getTargetAssocs(referrer, assocType);
|
||||
|
||||
return assocs.isEmpty() ? null : assocs.get(0).getTargetRef();
|
||||
}
|
||||
}
|
||||
|
||||
@Override public Map<QName, Serializable> getReferredProperties(NodeRef referrer, final QName aspectName)
|
||||
{
|
||||
final NodeRef referentNode = getReferentNode(referrer, aspectName);
|
||||
|
||||
if (referentNode == null)
|
||||
{
|
||||
throw new ReferentNodeNotFound("No referent node found for " + referrer + " " + aspectName);
|
||||
}
|
||||
else
|
||||
{
|
||||
final Map<QName, Serializable> allProps = nodeService.getProperties(referentNode);
|
||||
final 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 getReferredProperty(NodeRef referrer, 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 TypeMetadataReferralUnsupported(msg.toString());
|
||||
}
|
||||
|
||||
final Map<QName, Serializable> allPropValues = getReferredProperties(referrer, aspectDefn.getName());
|
||||
return allPropValues.get(propertyName);
|
||||
}
|
||||
|
||||
@Override public boolean hasReferredAspect(NodeRef referrer, QName aspectName)
|
||||
{
|
||||
final NodeRef referentNode = getReferentNode(referrer, aspectName);
|
||||
|
||||
if (referentNode == null)
|
||||
{
|
||||
throw new ReferentNodeNotFound("No referent node found for " + referrer + " " + aspectName);
|
||||
}
|
||||
else
|
||||
{
|
||||
return nodeService.hasAspect(referentNode, aspectName);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public Map<MetadataReferral, NodeRef> getAttachedReferrals(NodeRef potentialReferrer)
|
||||
{
|
||||
Set<MetadataReferral> metadataReferrals = referralAdminService.getAttachedReferralsFrom(potentialReferrer);
|
||||
|
||||
Map<MetadataReferral, NodeRef> result = new HashMap<>();
|
||||
for (MetadataReferral mr : metadataReferrals)
|
||||
{
|
||||
// We need only use the first aspect to get the MetadataReferral object
|
||||
if (!mr.getAspects().isEmpty())
|
||||
{
|
||||
result.put(mr, getReferentNode(potentialReferrer, mr.getAspects().iterator().next()));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2015 Alfresco Software Limited.
|
||||
*
|
||||
* This file is part of Alfresco
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* This package contains the types that deliver the Metadata Referral feature.
|
||||
* Metadata referral allows node metadata to be shared between multiple Alfresco nodes.
|
||||
* <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/>
|
||||
* 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.
|
||||
* <p/>
|
||||
* Multiple nodes may share the same referent node and one node may be linked to multiple referrers.
|
||||
* <p/>
|
||||
* 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 referent via the association.
|
||||
* <p/>
|
||||
* See {@link org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferralAdminService}
|
||||
* for details on how to create and destroy metadata links between nodes.
|
||||
* <p/>
|
||||
* See {@link org.alfresco.module.org_alfresco_module_rm.referredmetadata.ReferredMetadataService}
|
||||
* for details on how the data access is performed.
|
||||
* <p/>
|
||||
* 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.referredmetadata;
|
@@ -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<Map<QName, Serializable>> propertiesCaptor;
|
||||
|
||||
|
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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 2.4.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);
|
||||
}
|
||||
}
|
@@ -19,37 +19,32 @@
|
||||
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
|
||||
* @author Neil Mc Erlean
|
||||
* @since 2.4.a
|
||||
*/
|
||||
public class ClassifiedAspectUnitTest implements ClassifiedContentModel
|
||||
@@ -57,14 +52,13 @@ 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<QName, Serializable> oldProps = new HashMap<>();
|
||||
oldProps.put(PROP_CLASSIFIED_BY, "userone");
|
||||
oldProps.put(PROP_CURRENT_CLASSIFICATION, "Top Secret");
|
||||
Map<QName, Serializable> 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, ASPECT_CLASSIFIED);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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.referredmetadata;
|
||||
|
||||
import static java.util.Collections.emptySet;
|
||||
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;
|
||||
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.NodeService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link MetadataReferral}.
|
||||
*
|
||||
* @author Neil Mc Erlean
|
||||
* @since 3.0.a
|
||||
*/
|
||||
public class MetadataReferralUnitTest
|
||||
{
|
||||
@Mock DictionaryService mockDictionaryService;
|
||||
@Mock NodeService mockNodeService;
|
||||
|
||||
private final ReferralAdminServiceImpl referralAdminService = new ReferralAdminServiceImpl();
|
||||
|
||||
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);
|
||||
|
||||
referralAdminService.setNodeService(mockNodeService);
|
||||
}
|
||||
|
||||
@Test public void nullOrEmptyReferralsAreForbidden()
|
||||
{
|
||||
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)
|
||||
public void referralMustHaveAssocThatExists()
|
||||
{
|
||||
when(mockDictionaryService.getAssociation(assoc1)).thenReturn(null);
|
||||
when(mockDictionaryService.getAspect(aspect1)).thenReturn(mock(AspectDefinition.class));
|
||||
|
||||
MetadataReferral mr = new MetadataReferral();
|
||||
mr.setAssocType(assoc1);
|
||||
mr.setAspects(asSet(aspect1));
|
||||
mr.setDictionaryService(mockDictionaryService);
|
||||
mr.validateAndRegister();
|
||||
}
|
||||
|
||||
@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);
|
||||
|
||||
MetadataReferral mr = new MetadataReferral();
|
||||
mr.setAssocType(assoc1);
|
||||
mr.setAspects(asSet(aspect1, aspect2));
|
||||
mr.setDictionaryService(mockDictionaryService);
|
||||
mr.validateAndRegister();
|
||||
}
|
||||
}
|
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* 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.referredmetadata;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
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;
|
||||
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 ReferralAdminServiceImpl}.
|
||||
*
|
||||
* @author Neil Mc Erlean
|
||||
* @since 2.4.a
|
||||
*/
|
||||
public class ReferralAdminServiceImplUnitTest
|
||||
{
|
||||
@InjectMocks private final ReferralAdminServiceImpl referralAdminService = new ReferralAdminServiceImpl();
|
||||
|
||||
@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 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 MetadataReferral referral1 = new MetadataReferral()
|
||||
{{
|
||||
this.setAssocType(assoc1);
|
||||
this.setAspects(asSet(aspect1, aspect2));
|
||||
}};
|
||||
private final MetadataReferral referral2 = new MetadataReferral()
|
||||
{{
|
||||
this.setAssocType(assoc2);
|
||||
this.setAspects(asSet(aspect3));
|
||||
}};
|
||||
|
||||
@Before public void setUp()
|
||||
{
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
when(mockRegistry.getMetadataReferrals()).thenReturn(asSet(referral1, referral2));
|
||||
}
|
||||
|
||||
@Test(expected=IllegalArgumentException.class)
|
||||
public void attachingReferrerWithNoAspectConfiguredShouldFail()
|
||||
{
|
||||
referralAdminService.attachReferrer(node2, node1, aspect1);
|
||||
}
|
||||
|
||||
@Test public void attachDetach()
|
||||
{
|
||||
when(mockRegistry.getReferralForAspect(aspect1)).thenReturn(referral1);
|
||||
|
||||
// attach
|
||||
MetadataReferral d = attachReferrer(node1, node2, aspect1);
|
||||
|
||||
// validate
|
||||
assertEquals(assoc1, d.getAssocType());
|
||||
assertEquals(asSet(aspect1, aspect2), d.getAspects());
|
||||
assertTrue(mockReferredMetadataService.isReferringMetadata(node1, aspect1));
|
||||
assertFalse(mockReferredMetadataService.isReferringMetadata(node1, aspect3));
|
||||
|
||||
// detach
|
||||
assertEquals(d, referralAdminService.detachReferrer(node1, aspect1));
|
||||
}
|
||||
|
||||
private MetadataReferral attachReferrer(NodeRef referrer, NodeRef referent, QName aspectName)
|
||||
{
|
||||
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 : mr.getAspects())
|
||||
{
|
||||
when(mockReferredMetadataService.isReferringMetadata(referrer, aspect)).thenReturn(true);
|
||||
}
|
||||
return mr;
|
||||
}
|
||||
|
||||
@Test public void chainsOfDelegationShouldBePrevented()
|
||||
{
|
||||
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
|
||||
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(ChainedMetadataReferralUnsupported.class, () -> {
|
||||
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, aspect1);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* 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.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;
|
||||
@Mock ReferralRegistry mockReferralRegistry;
|
||||
|
||||
/** 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(mockReferralRegistry.getReferralForAspect(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<QName, Serializable>()
|
||||
{{
|
||||
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<QName, Serializable> 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<MetadataReferral, NodeRef> expectedReferrals = new HashMap<>();
|
||||
expectedReferrals.put(referral, referentNode);
|
||||
|
||||
assertEquals(expectedReferrals, referredMetadataService.getAttachedReferrals(referringNode));
|
||||
assertEquals(emptyMap(), referredMetadataService.getAttachedReferrals(nodeWithoutReferent));
|
||||
}
|
||||
}
|
@@ -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.test.util;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
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 2.4.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 <T> the type of elements in the list.
|
||||
* @return the list with each element being the first retrieved from a {@code Supplier}.
|
||||
*/
|
||||
public static <T> List<T> asListFrom(Supplier<T>... suppliers)
|
||||
{
|
||||
if (suppliers == null || suppliers.length == 0)
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
else
|
||||
{
|
||||
return Stream.of(suppliers)
|
||||
.map(s -> s.get())
|
||||
.collect(toList());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 <T> 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 <T> Set<T> asSetFrom(Supplier<T>... suppliers)
|
||||
{
|
||||
List<T> l = asListFrom(suppliers);
|
||||
return new HashSet<>(l);
|
||||
}
|
||||
|
||||
/**
|
||||
* This utility method converts a vararg of objects into a Set<T>.
|
||||
*
|
||||
* @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 <T> Set<T> asSet(T... objects)
|
||||
{
|
||||
return new HashSet<>(asList(objects));
|
||||
}
|
||||
}
|
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.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;
|
||||
|
||||
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 2.4.a
|
||||
*/
|
||||
public class FPUtilsUnitTest
|
||||
{
|
||||
@Test public void asListShouldProduceList()
|
||||
{
|
||||
List<String> 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.<String>asListFrom());
|
||||
}
|
||||
|
||||
@Test public void asSetShouldProduceSet()
|
||||
{
|
||||
assertEquals(new HashSet<>(asList("hello", "world")),
|
||||
asSet("hello", "hello", "world"));
|
||||
}
|
||||
|
||||
@Test public void asSetFromShouldWork()
|
||||
{
|
||||
Set<String> s = asSetFrom(() -> "hello",
|
||||
() -> "hello",
|
||||
() -> "world",
|
||||
() -> {
|
||||
String s1 = "wo";
|
||||
String s2 = "rld";
|
||||
return s1 + s2;
|
||||
});
|
||||
assertEquals(new HashSet<>(asList("hello", "world")), s);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user