Merged 1.4 to HEAD

svn merge svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@4329 svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@4331 .
   svn merge svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@4332 svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@4339 .


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@4653 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley
2006-12-19 12:35:52 +00:00
parent 00e6c989fc
commit 473c9ff1ff
20 changed files with 751 additions and 64 deletions

View File

@@ -18,7 +18,9 @@ package org.alfresco.repo.node.integrity;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -30,14 +32,17 @@ import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.TransactionListenerAdapter;
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.dictionary.PropertyDefinition;
import org.alfresco.service.cmr.dictionary.TypeDefinition;
import org.alfresco.service.cmr.repository.AssociationRef;
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.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.util.PropertyCheck;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -52,12 +57,16 @@ public class IncompleteNodeTagger
implements NodeServicePolicies.OnCreateNodePolicy,
NodeServicePolicies.OnUpdatePropertiesPolicy,
NodeServicePolicies.OnAddAspectPolicy,
NodeServicePolicies.OnRemoveAspectPolicy
NodeServicePolicies.OnRemoveAspectPolicy,
NodeServicePolicies.OnCreateChildAssociationPolicy,
NodeServicePolicies.OnDeleteChildAssociationPolicy,
NodeServicePolicies.OnCreateAssociationPolicy,
NodeServicePolicies.OnDeleteAssociationPolicy
{
private static Log logger = LogFactory.getLog(IncompleteNodeTagger.class);
/** key against which the set of nodes to check is stored in the current transaction */
private static final String KEY_NODE_SET = "IncompleteNodeTagger.NodeSet";
private static final String KEY_NODES = "IncompleteNodeTagger.Nodes";
private PolicyComponent policyComponent;
private DictionaryService dictionaryService;
@@ -118,48 +127,107 @@ public class IncompleteNodeTagger
QName.createQName(NamespaceService.ALFRESCO_URI, "onRemoveAspect"),
this,
new JavaBehaviour(this, "onRemoveAspect"));
policyComponent.bindAssociationBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateChildAssociation"),
this,
new JavaBehaviour(this, "onCreateChildAssociation"));
policyComponent.bindAssociationBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "onDeleteChildAssociation"),
this,
new JavaBehaviour(this, "onDeleteChildAssociation"));
policyComponent.bindAssociationBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateAssociation"),
this,
new JavaBehaviour(this, "onCreateAssociation"));
policyComponent.bindAssociationBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "onDeleteAssociation"),
this,
new JavaBehaviour(this, "onDeleteAssociation"));
}
/**
* @return Returns the set of nodes to check, or null if none were registered
* @return Returns the set of nodes to check properties, or null if none were registered
*/
@SuppressWarnings("unchecked")
private Set<NodeRef> getNodeSet()
private Map<NodeRef, Set<QName>> getNodes()
{
return (Set<NodeRef>) AlfrescoTransactionSupport.getResource(KEY_NODE_SET);
return (Map<NodeRef, Set<QName>>) AlfrescoTransactionSupport.getResource(KEY_NODES);
}
/**
* Ensures that this service is registered with the transaction and saves the node
* reference for use later.
* reference for use (property check) later.
*
* @param nodeRef
*/
private void save(NodeRef nodeRef)
private Set<QName> save(NodeRef nodeRef)
{
Set<QName> assocs = null;
// register this service
AlfrescoTransactionSupport.bindListener(this);
// get the event list
Set<NodeRef> nodeRefs = getNodeSet();
if (nodeRefs == null)
Map<NodeRef, Set<QName>> nodes = getNodes();
if (nodes == null)
{
nodeRefs = new HashSet<NodeRef>(31, 0.75F);
AlfrescoTransactionSupport.bindResource(KEY_NODE_SET, nodeRefs);
nodes = new HashMap<NodeRef, Set<QName>>(31, 0.75F);
AlfrescoTransactionSupport.bindResource(KEY_NODES, nodes);
}
// add node to the set
nodeRefs.add(nodeRef);
if (nodes.containsKey(nodeRef))
{
assocs = nodes.get(nodeRef);
}
else
{
nodes.put(nodeRef, null);
}
// done
if (logger.isDebugEnabled())
{
logger.debug("Added node reference to set: " + nodeRef);
logger.debug("Added node reference to property set: " + nodeRef);
}
return assocs;
}
/**
* Ensures that this service is registered with the transaction and saves the node
* reference for use (association check) later.
*
* @param nodeRef
* @param assocType
*/
private void saveAssoc(NodeRef nodeRef, QName assocType)
{
// register this service
AlfrescoTransactionSupport.bindListener(this);
Set<QName> assocs = save(nodeRef);
if (assocs == null)
{
assocs = new HashSet<QName>(7, 0.75f);
Map<NodeRef, Set<QName>> nodes = getNodes();
nodes.put(nodeRef, assocs);
}
if (assocType != null)
{
assocs.add(assocType);
}
// done
if (logger.isDebugEnabled())
{
logger.debug("Added association to node: " + nodeRef + ", " + assocType);
}
}
public void onCreateNode(ChildAssociationRef childAssocRef)
{
NodeRef nodeRef = childAssocRef.getChildRef();
save(nodeRef);
saveAssoc(nodeRef, null);
}
public void onUpdateProperties(
@@ -202,28 +270,69 @@ public class IncompleteNodeTagger
save(nodeRef);
}
/**
* @see AssocSourceTypeIntegrityEvent
* @see AssocTargetTypeIntegrityEvent
* @see AssocSourceMultiplicityIntegrityEvent
* @see AssocTargetMultiplicityIntegrityEvent
* @see AssocTargetRoleIntegrityEvent
*/
public void onCreateChildAssociation(ChildAssociationRef childAssocRef)
{
saveAssoc(childAssocRef.getParentRef(), childAssocRef.getTypeQName());
}
/**
* @see AssocSourceMultiplicityIntegrityEvent
* @see AssocTargetMultiplicityIntegrityEvent
*/
public void onDeleteChildAssociation(ChildAssociationRef childAssocRef)
{
saveAssoc(childAssocRef.getParentRef(), childAssocRef.getTypeQName());
}
/**
* @see AssocSourceTypeIntegrityEvent
* @see AssocTargetTypeIntegrityEvent
* @see AssocSourceMultiplicityIntegrityEvent
* @see AssocTargetMultiplicityIntegrityEvent
*/
public void onCreateAssociation(AssociationRef nodeAssocRef)
{
saveAssoc(nodeAssocRef.getSourceRef(), nodeAssocRef.getTypeQName());
}
/**
* @see AssocSourceMultiplicityIntegrityEvent
* @see AssocTargetMultiplicityIntegrityEvent
*/
public void onDeleteAssociation(AssociationRef nodeAssocRef)
{
saveAssoc(nodeAssocRef.getSourceRef(), nodeAssocRef.getTypeQName());
}
/**
* Process all the nodes that require checking within the transaction.
*/
@Override
public void beforeCommit(boolean readOnly)
{
Set<NodeRef> nodeRefs = getNodeSet();
Map<NodeRef, Set<QName>> nodes = getNodes();
// clear the set out of the transaction
// there may be processes that react to the addition/removal of the aspect,
// and these will, in turn, lead to further events
AlfrescoTransactionSupport.unbindResource(KEY_NODE_SET);
AlfrescoTransactionSupport.unbindResource(KEY_NODES);
// process each node
for (NodeRef nodeRef : nodeRefs)
for (Map.Entry<NodeRef, Set<QName>> entry : nodes.entrySet())
{
if (nodeService.exists(nodeRef))
if (nodeService.exists(entry.getKey()))
{
processNode(nodeRef);
processNode(entry.getKey(), entry.getValue());
}
}
}
private void processNode(NodeRef nodeRef)
private void processNode(NodeRef nodeRef, Set<QName> assocTypes)
{
// ignore the node if the marker aspect is already present
boolean isTagged = nodeService.hasAspect(nodeRef, ContentModel.ASPECT_INCOMPLETE);
@@ -267,7 +376,46 @@ public class IncompleteNodeTagger
return;
}
}
// all properties passed (both class- and aspect-defined) - remove aspect
// test associations
if (assocTypes != null)
{
Map<QName, AssociationDefinition> assocDefs = typeDef.getAssociations();
if (assocTypes.size() > 0)
{
// check only those associations that have changed
for (QName assocType : assocTypes)
{
AssociationDefinition assocDef = assocDefs.get(assocType);
if (assocDef != null)
{
if (!checkAssociation(nodeRef, assocDef))
{
addOrRemoveTag(nodeRef, true, isTagged);
return;
}
}
}
}
else
{
// check all associations (typically for new objects)
for (QName assocType : assocDefs.keySet())
{
AssociationDefinition assocDef = assocDefs.get(assocType);
if (assocDef != null)
{
if (!checkAssociation(nodeRef, assocDef))
{
addOrRemoveTag(nodeRef, true, isTagged);
return;
}
}
}
}
}
// all properties and associations passed (both class- and aspect-defined) - remove aspect
addOrRemoveTag(nodeRef, false, isTagged);
}
@@ -304,6 +452,41 @@ public class IncompleteNodeTagger
// all properties were present
return true;
}
/**
* @param nodeRef
* @param assocDef
* @return
*/
private boolean checkAssociation(NodeRef nodeRef, AssociationDefinition assocDef)
{
boolean complete = true;
if (assocDef.isTargetMandatory() && !assocDef.isTargetMandatoryEnforced())
{
int actualSize = 0;
if (assocDef.isChild())
{
// check the child assocs present
List<ChildAssociationRef> childAssocRefs = nodeService.getChildAssocs(
nodeRef,
assocDef.getName(),
RegexQNamePattern.MATCH_ALL);
actualSize = childAssocRefs.size();
}
else
{
// check the target assocs present
List<AssociationRef> targetAssocRefs = nodeService.getTargetAssocs(nodeRef, assocDef.getName());
actualSize = targetAssocRefs.size();
}
if (assocDef.isTargetMandatory() && actualSize == 0)
{
complete = false;
}
}
return complete;
}
/**
* Adds or removes the {@link ContentModel#ASPECT_INCOMPLETE incomplete} marker aspect.

View File

@@ -118,7 +118,7 @@ public class IncompleteNodeTaggerTest extends TestCase
tagger.beforeCommit(false);
assertEquals(nodeService.hasAspect(nodeRef, ContentModel.ASPECT_INCOMPLETE), mustBeTagged);
}
public void testCreateWithoutProperties() throws Exception
{
NodeRef nodeRef = createNode("abc", IntegrityTest.TEST_TYPE_WITH_PROPERTIES, null);
@@ -130,4 +130,22 @@ public class IncompleteNodeTaggerTest extends TestCase
NodeRef nodeRef = createNode("abc", IntegrityTest.TEST_TYPE_WITH_PROPERTIES, properties);
checkTagging(nodeRef, false);
}
public void testCreateWithoutAssoc() throws Exception
{
NodeRef nodeRef = createNode("abc", IntegrityTest.TEST_TYPE_WITH_NON_ENFORCED_CHILD_ASSOCS, properties);
checkTagging(nodeRef, true);
}
public void testCreateWithAssoc() throws Exception
{
NodeRef nodeRef = createNode("abc", IntegrityTest.TEST_TYPE_WITH_NON_ENFORCED_CHILD_ASSOCS, properties);
nodeService.createNode(nodeRef,
IntegrityTest.TEST_ASSOC_CHILD_NON_ENFORCED,
QName.createQName(IntegrityTest.NAMESPACE, "easyas"),
IntegrityTest.TEST_TYPE_WITHOUT_ANYTHING,
null
);
checkTagging(nodeRef, false);
}
}

View File

@@ -62,12 +62,14 @@ public class IntegrityTest extends TestCase
public static final QName TEST_TYPE_WITH_PROPERTIES = QName.createQName(NAMESPACE, "typeWithProperties");
public static final QName TEST_TYPE_WITH_ASSOCS = QName.createQName(NAMESPACE, "typeWithAssocs");
public static final QName TEST_TYPE_WITH_CHILD_ASSOCS = QName.createQName(NAMESPACE, "typeWithChildAssocs");
public static final QName TEST_TYPE_WITH_NON_ENFORCED_CHILD_ASSOCS = QName.createQName(NAMESPACE, "typeWithNonEnforcedChildAssocs");
public static final QName TEST_ASSOC_NODE_ZEROMANY_ZEROMANY = QName.createQName(NAMESPACE, "assoc-0to* - 0to*");
public static final QName TEST_ASSOC_CHILD_ZEROMANY_ZEROMANY = QName.createQName(NAMESPACE, "child-0to* - 0to*");
public static final QName TEST_ASSOC_NODE_ONE_ONE = QName.createQName(NAMESPACE, "assoc-1to1 - 1to1");
public static final QName TEST_ASSOC_CHILD_ONE_ONE = QName.createQName(NAMESPACE, "child-1to1 - 1to1");
public static final QName TEST_ASSOC_ASPECT_ONE_ONE = QName.createQName(NAMESPACE, "aspect-assoc-1to1 - 1to1");
public static final QName TEST_ASSOC_CHILD_NON_ENFORCED = QName.createQName(NAMESPACE, "child-non-enforced");
public static final QName TEST_ASPECT_WITH_PROPERTIES = QName.createQName(NAMESPACE, "aspectWithProperties");
public static final QName TEST_ASPECT_WITH_ASSOC = QName.createQName(NAMESPACE, "aspectWithAssoc");

View File

@@ -106,6 +106,25 @@
</child-association>
</associations>
</type>
<!-- Type with mandatory non-enforced child assocs -->
<type name="test:typeWithNonEnforcedChildAssocs">
<title>Type With Child Assocs</title>
<parent>sys:base</parent>
<associations>
<child-association name="test:child-non-enforced">
<source>
<mandatory>false</mandatory>
<many>true</many>
</source>
<target>
<class>test:typeWithoutAnything</class>
<mandatory>true</mandatory>
<many>true</many>
</target>
<duplicate>false</duplicate>
</child-association>
</associations>
</type>
</types>
<aspects>