mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
Big honkin' merge from head. Sheesh!
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/WCM-DEV2/root@3617 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -42,6 +42,7 @@ import org.alfresco.repo.node.NodeServicePolicies.OnCreateStorePolicy;
|
||||
import org.alfresco.repo.node.NodeServicePolicies.OnDeleteAssociationPolicy;
|
||||
import org.alfresco.repo.node.NodeServicePolicies.OnDeleteChildAssociationPolicy;
|
||||
import org.alfresco.repo.node.NodeServicePolicies.OnDeleteNodePolicy;
|
||||
import org.alfresco.repo.node.NodeServicePolicies.OnMoveNodePolicy;
|
||||
import org.alfresco.repo.node.NodeServicePolicies.OnRemoveAspectPolicy;
|
||||
import org.alfresco.repo.node.NodeServicePolicies.OnUpdateNodePolicy;
|
||||
import org.alfresco.repo.node.NodeServicePolicies.OnUpdatePropertiesPolicy;
|
||||
@@ -99,6 +100,7 @@ public abstract class AbstractNodeServiceImpl implements NodeService
|
||||
private ClassPolicyDelegate<OnCreateStorePolicy> onCreateStoreDelegate;
|
||||
private ClassPolicyDelegate<BeforeCreateNodePolicy> beforeCreateNodeDelegate;
|
||||
private ClassPolicyDelegate<OnCreateNodePolicy> onCreateNodeDelegate;
|
||||
private ClassPolicyDelegate<OnMoveNodePolicy> onMoveNodeDelegate;
|
||||
private ClassPolicyDelegate<BeforeUpdateNodePolicy> beforeUpdateNodeDelegate;
|
||||
private ClassPolicyDelegate<OnUpdateNodePolicy> onUpdateNodeDelegate;
|
||||
private ClassPolicyDelegate<OnUpdatePropertiesPolicy> onUpdatePropertiesDelegate;
|
||||
@@ -169,6 +171,7 @@ public abstract class AbstractNodeServiceImpl implements NodeService
|
||||
onCreateStoreDelegate = policyComponent.registerClassPolicy(NodeServicePolicies.OnCreateStorePolicy.class);
|
||||
beforeCreateNodeDelegate = policyComponent.registerClassPolicy(NodeServicePolicies.BeforeCreateNodePolicy.class);
|
||||
onCreateNodeDelegate = policyComponent.registerClassPolicy(NodeServicePolicies.OnCreateNodePolicy.class);
|
||||
onMoveNodeDelegate = policyComponent.registerClassPolicy(NodeServicePolicies.OnMoveNodePolicy.class);
|
||||
beforeUpdateNodeDelegate = policyComponent.registerClassPolicy(NodeServicePolicies.BeforeUpdateNodePolicy.class);
|
||||
onUpdateNodeDelegate = policyComponent.registerClassPolicy(NodeServicePolicies.OnUpdateNodePolicy.class);
|
||||
onUpdatePropertiesDelegate = policyComponent.registerClassPolicy(NodeServicePolicies.OnUpdatePropertiesPolicy.class);
|
||||
@@ -235,6 +238,19 @@ public abstract class AbstractNodeServiceImpl implements NodeService
|
||||
policy.onCreateNode(childAssocRef);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see NodeServicePolicies.OnMoveNodePolicy#onMoveNode(ChildAssociationRef, ChildAssociationRef)
|
||||
*/
|
||||
protected void invokeOnMoveNode(ChildAssociationRef oldChildAssocRef, ChildAssociationRef newChildAssocRef)
|
||||
{
|
||||
NodeRef childNodeRef = newChildAssocRef.getChildRef();
|
||||
// get qnames to invoke against
|
||||
Set<QName> qnames = getTypeAndAspectQNames(childNodeRef);
|
||||
// execute policy for node type and aspects
|
||||
NodeServicePolicies.OnMoveNodePolicy policy = onMoveNodeDelegate.get(childNodeRef, qnames);
|
||||
policy.onMoveNode(oldChildAssocRef, newChildAssocRef);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see NodeServicePolicies.BeforeUpdateNodePolicy#beforeUpdateNode(NodeRef)
|
||||
*/
|
||||
@@ -326,7 +342,7 @@ public abstract class AbstractNodeServiceImpl implements NodeService
|
||||
/**
|
||||
* @see NodeServicePolicies.OnDeleteNodePolicy#onDeleteNode(ChildAssociationRef)
|
||||
*/
|
||||
protected void invokeOnDeleteNode(ChildAssociationRef childAssocRef, QName childNodeTypeQName, Set<QName> childAspectQnames)
|
||||
protected void invokeOnDeleteNode(ChildAssociationRef childAssocRef, QName childNodeTypeQName, Set<QName> childAspectQnames, boolean isArchivedNode)
|
||||
{
|
||||
// get qnames to invoke against
|
||||
Set<QName> qnames = new HashSet<QName>(childAspectQnames.size() + 1);
|
||||
@@ -335,7 +351,7 @@ public abstract class AbstractNodeServiceImpl implements NodeService
|
||||
|
||||
// execute policy for node type and aspects
|
||||
NodeServicePolicies.OnDeleteNodePolicy policy = onDeleteNodeDelegate.get(childAssocRef.getChildRef(), qnames);
|
||||
policy.onDeleteNode(childAssocRef);
|
||||
policy.onDeleteNode(childAssocRef, isArchivedNode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -32,6 +32,7 @@ import org.alfresco.repo.content.MimetypeMap;
|
||||
import org.alfresco.repo.dictionary.DictionaryComponent;
|
||||
import org.alfresco.repo.dictionary.DictionaryDAO;
|
||||
import org.alfresco.repo.dictionary.M2Model;
|
||||
import org.alfresco.repo.domain.hibernate.ChildAssocImpl;
|
||||
import org.alfresco.repo.domain.hibernate.NodeImpl;
|
||||
import org.alfresco.repo.node.db.NodeDaoService;
|
||||
import org.alfresco.repo.policy.JavaBehaviour;
|
||||
@@ -47,6 +48,7 @@ import org.alfresco.service.cmr.repository.AssociationRef;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.ContentData;
|
||||
import org.alfresco.service.cmr.repository.CyclicChildRelationshipException;
|
||||
import org.alfresco.service.cmr.repository.DuplicateChildNodeNameException;
|
||||
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
@@ -107,6 +109,7 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
|
||||
public static final QName PROP_QNAME_PROP1 = QName.createQName(NAMESPACE, "prop1");
|
||||
public static final QName PROP_QNAME_PROP2 = QName.createQName(NAMESPACE, "prop2");
|
||||
public static final QName ASSOC_TYPE_QNAME_TEST_CHILDREN = ContentModel.ASSOC_CHILDREN;
|
||||
public static final QName ASSOC_TYPE_QNAME_TEST_CONTAINS = ContentModel.ASSOC_CONTAINS;
|
||||
public static final QName ASSOC_TYPE_QNAME_TEST_NEXT = QName.createQName(NAMESPACE, "next");
|
||||
|
||||
public static final QName TYPE_QNAME_TEST_MULTIPLE_TESTER = QName.createQName(NAMESPACE, "multiple-tester");
|
||||
@@ -679,7 +682,7 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
|
||||
nodeService.addAspect(nodeRef, ASPECT_QNAME_TEST_TITLED, null);
|
||||
}
|
||||
|
||||
public void onDeleteNode(ChildAssociationRef childAssocRef)
|
||||
public void onDeleteNode(ChildAssociationRef childAssocRef, boolean isArchivedNode)
|
||||
{
|
||||
// add the child to the list
|
||||
deletedNodeRefs.add(childAssocRef.getChildRef());
|
||||
@@ -730,9 +733,10 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
|
||||
private int countChildrenOfNode(NodeRef nodeRef)
|
||||
{
|
||||
String query =
|
||||
"select node.childAssocs" +
|
||||
"select childAssoc" +
|
||||
" from " +
|
||||
NodeImpl.class.getName() + " node" +
|
||||
ChildAssocImpl.class.getName() + " childAssoc" +
|
||||
" join childAssoc.parent node" +
|
||||
" where node.uuid = ? and node.store.key.protocol = ? and node.store.key.identifier = ?";
|
||||
Session session = getSession();
|
||||
List results = session.createQuery(query)
|
||||
@@ -761,17 +765,24 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
|
||||
|
||||
public void testAddChild() throws Exception
|
||||
{
|
||||
NodeRef childNodeRef = nodeService.createNode(
|
||||
NodeRef childNodeRef1 = nodeService.createNode(
|
||||
rootNodeRef,
|
||||
ASSOC_TYPE_QNAME_TEST_CHILDREN,
|
||||
QName.createQName("pathA"),
|
||||
QName.createQName("C1"),
|
||||
ContentModel.TYPE_CONTAINER).getChildRef();
|
||||
int countBefore = countChildrenOfNode(rootNodeRef);
|
||||
assertEquals("Root children count incorrect", 1, countBefore);
|
||||
int count = countChildrenOfNode(rootNodeRef);
|
||||
assertEquals("Root children count incorrect", 1, count);
|
||||
NodeRef childNodeRef2 = nodeService.createNode(
|
||||
childNodeRef1,
|
||||
ASSOC_TYPE_QNAME_TEST_CHILDREN,
|
||||
QName.createQName("C2"),
|
||||
ContentModel.TYPE_CONTAINER).getChildRef();
|
||||
count = countChildrenOfNode(rootNodeRef);
|
||||
assertEquals("Root children count incorrect", 1, count);
|
||||
// associate the two nodes
|
||||
nodeService.addChild(
|
||||
rootNodeRef,
|
||||
childNodeRef,
|
||||
childNodeRef2,
|
||||
ASSOC_TYPE_QNAME_TEST_CHILDREN,
|
||||
QName.createQName("pathB"));
|
||||
// there should now be 2 child assocs on the root
|
||||
@@ -782,7 +793,7 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
|
||||
try
|
||||
{
|
||||
nodeService.addChild(
|
||||
childNodeRef,
|
||||
childNodeRef1,
|
||||
rootNodeRef,
|
||||
ASSOC_TYPE_QNAME_TEST_CHILDREN,
|
||||
QName.createQName("backToRoot"));
|
||||
@@ -807,10 +818,6 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
|
||||
QName.createQName("pathA"),
|
||||
ContentModel.TYPE_CONTAINER);
|
||||
NodeRef childARef = pathARef.getChildRef();
|
||||
ChildAssociationRef pathBRef = nodeService.addChild(
|
||||
parentRef, childARef, ASSOC_TYPE_QNAME_TEST_CHILDREN, QName.createQName("pathB"));
|
||||
ChildAssociationRef pathCRef = nodeService.addChild(
|
||||
parentRef, childARef, ASSOC_TYPE_QNAME_TEST_CHILDREN, QName.createQName("pathC"));
|
||||
AssociationRef pathDRef = nodeService.createAssociation(
|
||||
parentRef, childARef, ASSOC_TYPE_QNAME_TEST_NEXT);
|
||||
// remove the child - this must cascade
|
||||
@@ -826,24 +833,6 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
|
||||
0, nodeService.getTargetAssocs(parentRef, RegexQNamePattern.MATCH_ALL).size());
|
||||
}
|
||||
|
||||
public void testAddAndRemoveChild() throws Exception
|
||||
{
|
||||
ChildAssociationRef pathARef = nodeService.createNode(
|
||||
rootNodeRef,
|
||||
ASSOC_TYPE_QNAME_TEST_CHILDREN,
|
||||
QName.createQName("pathA"),
|
||||
ContentModel.TYPE_CONTAINER);
|
||||
NodeRef childRef = pathARef.getChildRef();
|
||||
// make a duplication, but non-primary, child associaton
|
||||
nodeService.addChild(
|
||||
rootNodeRef,
|
||||
pathARef.getChildRef(),
|
||||
pathARef.getTypeQName(),
|
||||
pathARef.getQName());
|
||||
// now remove the association - it will cascade to the child
|
||||
nodeService.removeChild(rootNodeRef, childRef);
|
||||
}
|
||||
|
||||
public enum TestEnum
|
||||
{
|
||||
TEST_ONE,
|
||||
@@ -1153,6 +1142,17 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
|
||||
RegexQNamePattern.MATCH_ALL);
|
||||
}
|
||||
|
||||
public static class MovePolicyTester implements NodeServicePolicies.OnMoveNodePolicy
|
||||
{
|
||||
public List<ChildAssociationRef> policyAssocRefs = new ArrayList<ChildAssociationRef>(2);
|
||||
public void onMoveNode(ChildAssociationRef oldChildAssocRef, ChildAssociationRef newChildAssocRef)
|
||||
{
|
||||
policyAssocRefs.add(oldChildAssocRef);
|
||||
policyAssocRefs.add(newChildAssocRef);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public void testMoveNode() throws Exception
|
||||
{
|
||||
Map<QName, ChildAssociationRef> assocRefs = buildNodeGraph();
|
||||
@@ -1163,12 +1163,24 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
|
||||
NodeRef n5Ref = n5pn7Ref.getParentRef();
|
||||
NodeRef n6Ref = n6pn8Ref.getParentRef();
|
||||
NodeRef n8Ref = n6pn8Ref.getChildRef();
|
||||
|
||||
MovePolicyTester policy = new MovePolicyTester();
|
||||
// bind to listen to the deletion of a node
|
||||
policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onMoveNode"),
|
||||
policy,
|
||||
new JavaBehaviour(policy, "onMoveNode"));
|
||||
|
||||
// move n8 to n5
|
||||
ChildAssociationRef assocRef = nodeService.moveNode(
|
||||
n8Ref,
|
||||
n5Ref,
|
||||
ASSOC_TYPE_QNAME_TEST_CHILDREN,
|
||||
QName.createQName(BaseNodeServiceTest.NAMESPACE, "n5_p_n8"));
|
||||
|
||||
// check that the move policy was fired
|
||||
assertEquals("Move policy not fired", 2, policy.policyAssocRefs.size());
|
||||
|
||||
// check that n6 is no longer the parent
|
||||
List<ChildAssociationRef> n6ChildRefs = nodeService.getChildAssocs(
|
||||
n6Ref,
|
||||
@@ -1498,4 +1510,137 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
|
||||
" total nodes: " + totalNodes + "\n" +
|
||||
" total assocs: " + totalAssocs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the duplicate child name is detected and thrown correctly
|
||||
*/
|
||||
public void testDuplicateCatch() throws Exception
|
||||
{
|
||||
NodeRef parentRef = nodeService.createNode(
|
||||
rootNodeRef,
|
||||
ASSOC_TYPE_QNAME_TEST_CHILDREN,
|
||||
QName.createQName("parent_child"),
|
||||
ContentModel.TYPE_CONTAINER).getChildRef();
|
||||
ChildAssociationRef pathARef = nodeService.createNode(
|
||||
parentRef,
|
||||
ASSOC_TYPE_QNAME_TEST_CONTAINS,
|
||||
QName.createQName("pathA"),
|
||||
ContentModel.TYPE_CONTENT);
|
||||
// no issue with this
|
||||
ChildAssociationRef pathBRef = nodeService.createNode(
|
||||
parentRef,
|
||||
ASSOC_TYPE_QNAME_TEST_CONTAINS,
|
||||
QName.createQName("pathB"),
|
||||
ContentModel.TYPE_CONTENT);
|
||||
AlfrescoTransactionSupport.flush();
|
||||
// there should be no issue with a duplicate association names any more
|
||||
ChildAssociationRef pathBDuplicateRef = nodeService.createNode(
|
||||
parentRef,
|
||||
ASSOC_TYPE_QNAME_TEST_CONTAINS,
|
||||
QName.createQName("pathB"),
|
||||
ContentModel.TYPE_CONTENT);
|
||||
AlfrescoTransactionSupport.flush();
|
||||
// Now create nodes with duplicate cm:name properties
|
||||
Map<QName, Serializable> props = new HashMap<QName, Serializable>(5);
|
||||
props.put(ContentModel.PROP_NAME, "ABC");
|
||||
ChildAssociationRef pathAbcRef = nodeService.createNode(
|
||||
parentRef,
|
||||
ASSOC_TYPE_QNAME_TEST_CONTAINS,
|
||||
QName.createQName("ABC"),
|
||||
ContentModel.TYPE_CONTENT,
|
||||
props);
|
||||
AlfrescoTransactionSupport.flush();
|
||||
try
|
||||
{
|
||||
// now check that the duplicate is detected with attention to being case-insensitive
|
||||
props.put(ContentModel.PROP_NAME, "abc");
|
||||
ChildAssociationRef pathAbcDuplicateRef = nodeService.createNode(
|
||||
parentRef,
|
||||
ASSOC_TYPE_QNAME_TEST_CONTAINS,
|
||||
QName.createQName("ABC-duplicate"),
|
||||
ContentModel.TYPE_CONTENT,
|
||||
props);
|
||||
fail("Failed to throw duplicate child name exception");
|
||||
}
|
||||
catch (DuplicateChildNodeNameException e)
|
||||
{
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the unique constraint doesn't break delete and create within the same
|
||||
* transaction.
|
||||
*/
|
||||
public void testDeleteAndAddSameName() throws Exception
|
||||
{
|
||||
NodeRef parentRef = nodeService.createNode(
|
||||
rootNodeRef,
|
||||
ASSOC_TYPE_QNAME_TEST_CHILDREN,
|
||||
QName.createQName("parent_child"),
|
||||
ContentModel.TYPE_CONTAINER).getChildRef();
|
||||
// create node ABC
|
||||
Map<QName, Serializable> props = new HashMap<QName, Serializable>(5);
|
||||
props.put(ContentModel.PROP_NAME, "ABC");
|
||||
ChildAssociationRef pathAbcRef = nodeService.createNode(
|
||||
parentRef,
|
||||
ASSOC_TYPE_QNAME_TEST_CONTAINS,
|
||||
QName.createQName("ABC"),
|
||||
ContentModel.TYPE_CONTENT,
|
||||
props);
|
||||
NodeRef abcRef = pathAbcRef.getChildRef();
|
||||
AlfrescoTransactionSupport.flush();
|
||||
// delete ABC
|
||||
nodeService.deleteNode(abcRef);
|
||||
// create it again
|
||||
pathAbcRef = nodeService.createNode(
|
||||
parentRef,
|
||||
ASSOC_TYPE_QNAME_TEST_CONTAINS,
|
||||
QName.createQName("ABC"),
|
||||
ContentModel.TYPE_CONTENT,
|
||||
props);
|
||||
// there should not be any failure when doing this in the same transaction
|
||||
}
|
||||
|
||||
public void testGetByName() throws Exception
|
||||
{
|
||||
NodeRef parentRef = nodeService.createNode(
|
||||
rootNodeRef,
|
||||
ASSOC_TYPE_QNAME_TEST_CHILDREN,
|
||||
QName.createQName("parent_child"),
|
||||
ContentModel.TYPE_CONTAINER).getChildRef();
|
||||
// create node ABC
|
||||
Map<QName, Serializable> props = new HashMap<QName, Serializable>(5);
|
||||
props.put(ContentModel.PROP_NAME, "ABC");
|
||||
ChildAssociationRef pathAbcRef = nodeService.createNode(
|
||||
parentRef,
|
||||
ASSOC_TYPE_QNAME_TEST_CONTAINS,
|
||||
QName.createQName("ABC"),
|
||||
ContentModel.TYPE_CONTENT,
|
||||
props);
|
||||
NodeRef abcRef = pathAbcRef.getChildRef();
|
||||
// create node DEF
|
||||
props.put(ContentModel.PROP_NAME, "DEF");
|
||||
ChildAssociationRef pathDefRef = nodeService.createNode(
|
||||
abcRef,
|
||||
ASSOC_TYPE_QNAME_TEST_CONTAINS,
|
||||
QName.createQName("DEF"),
|
||||
ContentModel.TYPE_CONTENT,
|
||||
props);
|
||||
NodeRef defRef = pathDefRef.getChildRef();
|
||||
|
||||
// now browse down using the node service
|
||||
NodeRef checkParentRef = nodeService.getChildByName(rootNodeRef, ASSOC_TYPE_QNAME_TEST_CHILDREN, parentRef.getId());
|
||||
assertNotNull("First level, non-named node not found", checkParentRef);
|
||||
assertEquals(parentRef, checkParentRef);
|
||||
NodeRef checkAbcRef = nodeService.getChildByName(checkParentRef, ASSOC_TYPE_QNAME_TEST_CONTAINS, "abc");
|
||||
assertNotNull("Second level, named node 'ABC' not found", checkAbcRef);
|
||||
assertEquals(abcRef, checkAbcRef);
|
||||
NodeRef checkDefRef = nodeService.getChildByName(checkAbcRef, ASSOC_TYPE_QNAME_TEST_CONTAINS, "def");
|
||||
assertNotNull("Third level, named node 'DEF' not found", checkDefRef);
|
||||
assertEquals(defRef, checkDefRef);
|
||||
// check that we get null where not present
|
||||
NodeRef checkHijRef = nodeService.getChildByName(checkAbcRef, ASSOC_TYPE_QNAME_TEST_CONTAINS, "hij");
|
||||
assertNull("Third level, named node 'HIJ' should not have been found", checkHijRef);
|
||||
}
|
||||
}
|
||||
|
@@ -82,6 +82,17 @@ public interface NodeServicePolicies
|
||||
public void onCreateNode(ChildAssociationRef childAssocRef);
|
||||
}
|
||||
|
||||
public interface OnMoveNodePolicy extends ClassPolicy
|
||||
{
|
||||
/**
|
||||
* Called when a node has been moved.
|
||||
*
|
||||
* @param oldChildAssocRef the child association reference prior to the move
|
||||
* @param newChildAssocRef the child association reference after the move
|
||||
*/
|
||||
public void onMoveNode(ChildAssociationRef oldChildAssocRef, ChildAssociationRef newChildAssocRef);
|
||||
}
|
||||
|
||||
public interface BeforeUpdateNodePolicy extends ClassPolicy
|
||||
{
|
||||
/**
|
||||
@@ -140,9 +151,10 @@ public interface NodeServicePolicies
|
||||
* which has been deleted and cannot be used to retrieve node or associaton
|
||||
* information from any of the services.
|
||||
*
|
||||
* @param childAssocRef the primary parent-child association of the deleted node
|
||||
* @param childAssocRef the primary parent-child association of the deleted node
|
||||
* @param isNodeArchived indicates whether the node has been archived rather than purged
|
||||
*/
|
||||
public void onDeleteNode(ChildAssociationRef childAssocRef);
|
||||
public void onDeleteNode(ChildAssociationRef childAssocRef, boolean isNodeArchived);
|
||||
}
|
||||
|
||||
public interface BeforeAddAspectPolicy extends ClassPolicy
|
||||
|
82
source/java/org/alfresco/repo/node/TemporaryAspect.java
Normal file
82
source/java/org/alfresco/repo/node/TemporaryAspect.java
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.repo.node;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.copy.CopyServicePolicies;
|
||||
import org.alfresco.repo.policy.JavaBehaviour;
|
||||
import org.alfresco.repo.policy.PolicyComponent;
|
||||
import org.alfresco.repo.policy.PolicyScope;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
/**
|
||||
* Registers and contains the behaviour specific to the
|
||||
* {@link org.alfresco.model.ContentModel#ASPECT_TEMPORARY temporary aspect}.
|
||||
*
|
||||
* @author gavinc
|
||||
*/
|
||||
public class TemporaryAspect implements CopyServicePolicies.OnCopyNodePolicy
|
||||
{
|
||||
// Dependencies
|
||||
private PolicyComponent policyComponent;
|
||||
|
||||
/**
|
||||
* @param policyComponent the policy component to register behaviour with
|
||||
*/
|
||||
public void setPolicyComponent(PolicyComponent policyComponent)
|
||||
{
|
||||
this.policyComponent = policyComponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the Temporary Aspect
|
||||
* <p>
|
||||
* Ensures that the {@link ContentModel#ASPECT_TEMPORARY temporary aspect}
|
||||
* copy behaviour is disabled when update copies are performed.
|
||||
*/
|
||||
public void init()
|
||||
{
|
||||
// disable copy for referencable aspect
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onCopyNode"),
|
||||
ContentModel.ASPECT_TEMPORARY,
|
||||
new JavaBehaviour(this, "onCopyNode"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing
|
||||
*/
|
||||
public void onCopyNode(
|
||||
QName classRef,
|
||||
NodeRef sourceNodeRef,
|
||||
StoreRef destinationStoreRef,
|
||||
boolean copyToNewNode,
|
||||
PolicyScope copyDetails)
|
||||
{
|
||||
if (copyToNewNode)
|
||||
{
|
||||
copyDetails.addAspect(ContentModel.ASPECT_TEMPORARY);
|
||||
}
|
||||
else
|
||||
{
|
||||
// don't copy if this is an update operation
|
||||
}
|
||||
}
|
||||
}
|
@@ -23,6 +23,7 @@ import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -41,7 +42,10 @@ import org.alfresco.repo.node.StoreArchiveMap;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
||||
import org.alfresco.service.cmr.dictionary.AspectDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.AssociationDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.ClassDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.InvalidAspectException;
|
||||
import org.alfresco.service.cmr.dictionary.InvalidTypeException;
|
||||
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
||||
@@ -62,8 +66,10 @@ import org.alfresco.service.cmr.repository.NodeRef.Status;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.service.namespace.QNamePattern;
|
||||
import org.alfresco.util.ParameterCheck;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -280,29 +286,35 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
String newId = generateGuid(properties);
|
||||
|
||||
// create the node instance
|
||||
Node node = nodeDaoService.newNode(store, newId, nodeTypeQName);
|
||||
Node childNode = nodeDaoService.newNode(store, newId, nodeTypeQName);
|
||||
|
||||
// get the parent node
|
||||
Node parentNode = getNodeNotNull(parentRef);
|
||||
|
||||
// create the association - invoke policy behaviour
|
||||
ChildAssoc childAssoc = nodeDaoService.newChildAssoc(parentNode, node, true, assocTypeQName, assocQName);
|
||||
ChildAssociationRef childAssocRef = childAssoc.getChildAssocRef();
|
||||
|
||||
// Set the default property values
|
||||
addDefaultPropertyValues(nodeTypeDef, properties);
|
||||
|
||||
// Add the default aspects to the node
|
||||
addDefaultAspects(nodeTypeDef, node, childAssocRef.getChildRef(), properties);
|
||||
addDefaultAspects(nodeTypeDef, childNode, properties);
|
||||
|
||||
// set the properties - it is a new node so only set properties if there are any
|
||||
Map<QName, Serializable> propertiesBefore = getProperties(childAssocRef.getChildRef());
|
||||
Map<QName, Serializable> propertiesBefore = getPropertiesImpl(childNode);
|
||||
Map<QName, Serializable> propertiesAfter = null;
|
||||
if (properties.size() > 0)
|
||||
{
|
||||
propertiesAfter = setPropertiesImpl(childAssocRef.getChildRef(), properties);
|
||||
propertiesAfter = setPropertiesImpl(childNode, properties);
|
||||
}
|
||||
|
||||
// create the association
|
||||
ChildAssoc childAssoc = nodeDaoService.newChildAssoc(
|
||||
parentNode,
|
||||
childNode,
|
||||
true,
|
||||
assocTypeQName,
|
||||
assocQName);
|
||||
setChildUniqueName(childNode); // ensure uniqueness
|
||||
ChildAssociationRef childAssocRef = childAssoc.getChildAssocRef();
|
||||
|
||||
// Invoke policy behaviour
|
||||
invokeOnCreateNode(childAssocRef);
|
||||
invokeOnUpdateNode(parentRef);
|
||||
@@ -320,8 +332,10 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
*
|
||||
* @param nodeTypeDef
|
||||
*/
|
||||
private void addDefaultAspects(ClassDefinition classDefinition, Node node, NodeRef nodeRef, Map<QName, Serializable> properties)
|
||||
private void addDefaultAspects(ClassDefinition classDefinition, Node node, Map<QName, Serializable> properties)
|
||||
{
|
||||
NodeRef nodeRef = node.getNodeRef();
|
||||
|
||||
// get the mandatory aspects for the node type
|
||||
List<AspectDefinition> defaultAspectDefs = classDefinition.getDefaultAspects();
|
||||
|
||||
@@ -335,7 +349,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
invokeOnAddAspect(nodeRef, defaultAspectDef.getName());
|
||||
|
||||
// Now add any default aspects for this aspect
|
||||
addDefaultAspects(defaultAspectDef, node, nodeRef, properties);
|
||||
addDefaultAspects(defaultAspectDef, node, properties);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -387,8 +401,16 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
// remove the child assoc from the old parent
|
||||
// don't cascade as we will still need the node afterwards
|
||||
nodeDaoService.deleteChildAssoc(oldAssoc, false);
|
||||
|
||||
// create a new assoc
|
||||
ChildAssoc newAssoc = nodeDaoService.newChildAssoc(newParentNode, nodeToMove, true, assocTypeQName, assocQName);
|
||||
ChildAssoc newAssoc = nodeDaoService.newChildAssoc(
|
||||
newParentNode,
|
||||
nodeToMove,
|
||||
true,
|
||||
assocTypeQName,
|
||||
assocQName);
|
||||
setChildUniqueName(nodeToMove); // ensure uniqueness
|
||||
ChildAssociationRef newAssocRef = newAssoc.getChildAssocRef();
|
||||
|
||||
// If the node is moving stores, then drag the node hierarchy with it
|
||||
if (movingStore)
|
||||
@@ -406,7 +428,9 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
// invoke policy behaviour
|
||||
if (movingStore)
|
||||
{
|
||||
invokeOnDeleteNode(oldAssocRef, nodeToMoveTypeQName, nodeToMoveAspects);
|
||||
// TODO for now indicate that the node has been archived to prevent the version history from being removed
|
||||
// in the future a onMove policy could be added and remove the need for onDelete and onCreate to be fired here
|
||||
invokeOnDeleteNode(oldAssocRef, nodeToMoveTypeQName, nodeToMoveAspects, true);
|
||||
invokeOnCreateNode(newAssoc.getChildAssocRef());
|
||||
}
|
||||
else
|
||||
@@ -416,6 +440,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
invokeOnUpdateNode(oldParentNode.getNodeRef());
|
||||
invokeOnUpdateNode(newParentRef);
|
||||
}
|
||||
invokeOnMoveNode(oldAssocRef, newAssocRef);
|
||||
|
||||
// update the node status
|
||||
nodeDaoService.recordChangeId(nodeToMoveRef);
|
||||
@@ -472,8 +497,8 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
node.setTypeQName(typeQName);
|
||||
|
||||
// Add the default aspects to the node (update the properties with any new default values)
|
||||
Map<QName, Serializable> properties = this.getProperties(nodeRef);
|
||||
addDefaultAspects(nodeTypeDef, node, nodeRef, properties);
|
||||
Map<QName, Serializable> properties = this.getPropertiesImpl(node);
|
||||
addDefaultAspects(nodeTypeDef, node, properties);
|
||||
this.setProperties(nodeRef, properties);
|
||||
|
||||
// Invoke policies
|
||||
@@ -503,7 +528,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
Node node = getNodeNotNull(nodeRef);
|
||||
|
||||
// attach the properties to the current node properties
|
||||
Map<QName, Serializable> nodeProperties = getProperties(nodeRef);
|
||||
Map<QName, Serializable> nodeProperties = getPropertiesImpl(node);
|
||||
|
||||
if (aspectProperties != null)
|
||||
{
|
||||
@@ -514,7 +539,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
addDefaultPropertyValues(aspectDef, nodeProperties);
|
||||
|
||||
// Add any dependant aspect
|
||||
addDefaultAspects(aspectDef, node, nodeRef, nodeProperties);
|
||||
addDefaultAspects(aspectDef, node, nodeProperties);
|
||||
|
||||
// Set the property values back on the node
|
||||
setProperties(nodeRef, nodeProperties);
|
||||
@@ -598,8 +623,11 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
|
||||
public void deleteNode(NodeRef nodeRef)
|
||||
{
|
||||
// Invoke policy behaviours
|
||||
invokeBeforeDeleteNode(nodeRef);
|
||||
boolean isArchivedNode = false;
|
||||
boolean requiresDelete = false;
|
||||
|
||||
// Invoke policy behaviours
|
||||
invokeBeforeDeleteNode(nodeRef);
|
||||
|
||||
// get the node
|
||||
Node node = getNodeNotNull(nodeRef);
|
||||
@@ -610,23 +638,41 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
Set<QName> nodeAspectQNames = node.getAspects();
|
||||
|
||||
// check if we need to archive the node
|
||||
StoreRef storeRef = nodeRef.getStoreRef();
|
||||
StoreRef archiveStoreRef = storeArchiveMap.getArchiveMap().get(storeRef);
|
||||
// get the type and check if we need archiving
|
||||
TypeDefinition typeDef = dictionaryService.getType(node.getTypeQName());
|
||||
if (typeDef == null || !typeDef.isArchive() || archiveStoreRef == null)
|
||||
StoreRef archiveStoreRef = null;
|
||||
if (nodeAspectQNames.contains(ContentModel.ASPECT_TEMPORARY))
|
||||
{
|
||||
// the node has the temporary aspect meaning
|
||||
// it can not be archived
|
||||
requiresDelete = true;
|
||||
isArchivedNode = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
StoreRef storeRef = nodeRef.getStoreRef();
|
||||
archiveStoreRef = storeArchiveMap.getArchiveMap().get(storeRef);
|
||||
// get the type and check if we need archiving
|
||||
TypeDefinition typeDef = dictionaryService.getType(node.getTypeQName());
|
||||
if (typeDef == null || !typeDef.isArchive() || archiveStoreRef == null)
|
||||
{
|
||||
requiresDelete = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (requiresDelete)
|
||||
{
|
||||
// perform a normal deletion
|
||||
nodeDaoService.deleteNode(node, true);
|
||||
isArchivedNode = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// archive it
|
||||
archiveNode(nodeRef, archiveStoreRef);
|
||||
isArchivedNode = true;
|
||||
}
|
||||
|
||||
// Invoke policy behaviours
|
||||
invokeOnDeleteNode(childAssocRef, nodeTypeQName, nodeAspectQNames);
|
||||
invokeOnDeleteNode(childAssocRef, nodeTypeQName, nodeAspectQNames, isArchivedNode);
|
||||
}
|
||||
|
||||
public ChildAssociationRef addChild(NodeRef parentRef, NodeRef childRef, QName assocTypeQName, QName assocQName)
|
||||
@@ -635,21 +681,19 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
invokeBeforeUpdateNode(parentRef);
|
||||
invokeBeforeCreateChildAssociation(parentRef, childRef, assocTypeQName, assocQName);
|
||||
|
||||
// check that both nodes belong to the same store
|
||||
if (!parentRef.getStoreRef().equals(childRef.getStoreRef()))
|
||||
{
|
||||
throw new InvalidNodeRefException("Parent and child nodes must belong to the same store: \n" +
|
||||
" parent: " + parentRef + "\n" +
|
||||
" child: " + childRef,
|
||||
childRef);
|
||||
}
|
||||
|
||||
// get the parent node and ensure that it is a container node
|
||||
Node parentNode = getNodeNotNull(parentRef);
|
||||
// get the child node
|
||||
Node childNode = getNodeNotNull(childRef);
|
||||
// make the association
|
||||
ChildAssoc assoc = nodeDaoService.newChildAssoc(parentNode, childNode, false, assocTypeQName, assocQName);
|
||||
ChildAssoc assoc = nodeDaoService.newChildAssoc(
|
||||
parentNode,
|
||||
childNode,
|
||||
false,
|
||||
assocTypeQName,
|
||||
assocQName);
|
||||
// ensure name uniqueness
|
||||
setChildUniqueName(childNode);
|
||||
ChildAssociationRef assocRef = assoc.getChildAssocRef();
|
||||
NodeRef childNodeRef = assocRef.getChildRef();
|
||||
|
||||
@@ -672,7 +716,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
|
||||
// get all the child assocs
|
||||
ChildAssociationRef primaryAssocRef = null;
|
||||
Collection<ChildAssoc> assocs = parentNode.getChildAssocs();
|
||||
Collection<ChildAssoc> assocs = nodeDaoService.getChildAssocs(parentNode);
|
||||
assocs = new HashSet<ChildAssoc>(assocs); // copy set as we will be modifying it
|
||||
for (ChildAssoc assoc : assocs)
|
||||
{
|
||||
@@ -710,6 +754,13 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
public Map<QName, Serializable> getProperties(NodeRef nodeRef) throws InvalidNodeRefException
|
||||
{
|
||||
Node node = getNodeNotNull(nodeRef);
|
||||
return getPropertiesImpl(node);
|
||||
}
|
||||
|
||||
private Map<QName, Serializable> getPropertiesImpl(Node node) throws InvalidNodeRefException
|
||||
{
|
||||
NodeRef nodeRef = node.getNodeRef();
|
||||
|
||||
Map<QName, PropertyValue> nodeProperties = node.getProperties();
|
||||
Map<QName, Serializable> ret = new HashMap<QName, Serializable>(nodeProperties.size());
|
||||
// copy values
|
||||
@@ -779,12 +830,16 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
*/
|
||||
public void setProperties(NodeRef nodeRef, Map<QName, Serializable> properties) throws InvalidNodeRefException
|
||||
{
|
||||
// Invoke policy behaviours
|
||||
Node node = getNodeNotNull(nodeRef);
|
||||
|
||||
// Invoke policy behaviours
|
||||
invokeBeforeUpdateNode(nodeRef);
|
||||
|
||||
// Do the set properties
|
||||
Map<QName, Serializable> propertiesBefore = getProperties(nodeRef);
|
||||
Map<QName, Serializable> propertiesAfter = setPropertiesImpl(nodeRef, properties);
|
||||
Map<QName, Serializable> propertiesBefore = getPropertiesImpl(node);
|
||||
Map<QName, Serializable> propertiesAfter = setPropertiesImpl(node, properties);
|
||||
|
||||
setChildUniqueName(node); // ensure uniqueness
|
||||
|
||||
// Invoke policy behaviours
|
||||
invokeOnUpdateNode(nodeRef);
|
||||
@@ -795,24 +850,18 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
* Does the work of setting the property values. Returns a map containing the state of the properties after the set
|
||||
* operation is complete.
|
||||
*
|
||||
* @param nodeRef the node reference
|
||||
* @param node the node
|
||||
* @param properties the map of property values
|
||||
* @return the map of property values after the set operation is complete
|
||||
* @throws InvalidNodeRefException
|
||||
*/
|
||||
private Map<QName, Serializable> setPropertiesImpl(NodeRef nodeRef, Map<QName, Serializable> properties) throws InvalidNodeRefException
|
||||
private Map<QName, Serializable> setPropertiesImpl(Node node, Map<QName, Serializable> properties) throws InvalidNodeRefException
|
||||
{
|
||||
if (properties == null)
|
||||
{
|
||||
throw new IllegalArgumentException("Properties may not be null");
|
||||
}
|
||||
ParameterCheck.mandatory("properties", properties);
|
||||
|
||||
// remove referencable properties
|
||||
removeReferencableProperties(properties);
|
||||
|
||||
// find the node
|
||||
Node node = getNodeNotNull(nodeRef);
|
||||
|
||||
// copy properties onto node
|
||||
Map<QName, PropertyValue> nodeProperties = node.getProperties();
|
||||
nodeProperties.clear();
|
||||
@@ -828,6 +877,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
}
|
||||
|
||||
// update the node status
|
||||
NodeRef nodeRef = node.getNodeRef();
|
||||
nodeDaoService.recordChangeId(nodeRef);
|
||||
|
||||
// Return the properties after
|
||||
@@ -847,10 +897,18 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
// Invoke policy behaviours
|
||||
invokeBeforeUpdateNode(nodeRef);
|
||||
|
||||
// Do the set operation
|
||||
Map<QName, Serializable> propertiesBefore = getProperties(nodeRef);
|
||||
Map<QName, Serializable> propertiesAfter = setPropertyImpl(nodeRef, qname, value);
|
||||
// get the node
|
||||
Node node = getNodeNotNull(nodeRef);
|
||||
|
||||
// Do the set operation
|
||||
Map<QName, Serializable> propertiesBefore = getPropertiesImpl(node);
|
||||
Map<QName, Serializable> propertiesAfter = setPropertyImpl(node, qname, value);
|
||||
|
||||
if (qname.equals(ContentModel.PROP_NAME))
|
||||
{
|
||||
setChildUniqueName(node); // ensure uniqueness
|
||||
}
|
||||
|
||||
// Invoke policy behaviours
|
||||
invokeOnUpdateNode(nodeRef);
|
||||
invokeOnUpdateProperties(nodeRef, propertiesBefore, propertiesAfter);
|
||||
@@ -860,16 +918,15 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
* Does the work of setting a property value. Returns the values of the properties after the set operation is
|
||||
* complete.
|
||||
*
|
||||
* @param nodeRef the node reference
|
||||
* @param node the node
|
||||
* @param qname the qname of the property
|
||||
* @param value the value of the property
|
||||
* @return the values of the properties after the set operation is complete
|
||||
* @throws InvalidNodeRefException
|
||||
*/
|
||||
public Map<QName, Serializable> setPropertyImpl(NodeRef nodeRef, QName qname, Serializable value) throws InvalidNodeRefException
|
||||
public Map<QName, Serializable> setPropertyImpl(Node node, QName qname, Serializable value) throws InvalidNodeRefException
|
||||
{
|
||||
// get the node
|
||||
Node node = getNodeNotNull(nodeRef);
|
||||
NodeRef nodeRef = node.getNodeRef();
|
||||
|
||||
Map<QName, PropertyValue> properties = node.getProperties();
|
||||
PropertyDefinition propertyDef = dictionaryService.getProperty(qname);
|
||||
@@ -880,7 +937,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
// update the node status
|
||||
nodeDaoService.recordChangeId(nodeRef);
|
||||
|
||||
return getProperties(nodeRef);
|
||||
return getPropertiesImpl(node);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -939,36 +996,50 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
{
|
||||
Node node = getNodeNotNull(nodeRef);
|
||||
// get the assocs pointing from it
|
||||
Collection<ChildAssoc> childAssocs = node.getChildAssocs();
|
||||
Collection<ChildAssociationRef> childAssocRefs = nodeDaoService.getChildAssocRefs(node);
|
||||
// shortcut if there are no assocs
|
||||
if (childAssocs.size() == 0)
|
||||
if (childAssocRefs.size() == 0)
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// sort results
|
||||
ArrayList<ChildAssoc> orderedList = new ArrayList<ChildAssoc>(childAssocs);
|
||||
ArrayList<ChildAssociationRef> orderedList = new ArrayList<ChildAssociationRef>(childAssocRefs);
|
||||
Collections.sort(orderedList);
|
||||
|
||||
// list of results
|
||||
List<ChildAssociationRef> results = new ArrayList<ChildAssociationRef>(childAssocs.size());
|
||||
int nthSibling = 0;
|
||||
for (ChildAssoc assoc : orderedList)
|
||||
Iterator<ChildAssociationRef> iterator = orderedList.iterator();
|
||||
while(iterator.hasNext())
|
||||
{
|
||||
ChildAssociationRef childAssocRef = iterator.next();
|
||||
// does the qname match the pattern?
|
||||
if (!qnamePattern.isMatch(assoc.getQname()) || !typeQNamePattern.isMatch(assoc.getTypeQName()))
|
||||
if (!qnamePattern.isMatch(childAssocRef.getQName()) || !typeQNamePattern.isMatch(childAssocRef.getTypeQName()))
|
||||
{
|
||||
// no match - ignore
|
||||
continue;
|
||||
// no match - remove
|
||||
iterator.remove();
|
||||
}
|
||||
else
|
||||
{
|
||||
childAssocRef.setNthSibling(nthSibling);
|
||||
nthSibling++;
|
||||
}
|
||||
ChildAssociationRef assocRef = assoc.getChildAssocRef();
|
||||
// slot the value in the right spot
|
||||
assocRef.setNthSibling(nthSibling);
|
||||
nthSibling++;
|
||||
// get the child
|
||||
results.add(assoc.getChildAssocRef());
|
||||
}
|
||||
// done
|
||||
return results;
|
||||
return orderedList;
|
||||
}
|
||||
|
||||
public NodeRef getChildByName(NodeRef nodeRef, QName assocTypeQName, String childName)
|
||||
{
|
||||
Node node = getNodeNotNull(nodeRef);
|
||||
ChildAssoc childAssoc = nodeDaoService.getChildAssoc(node, assocTypeQName, childName);
|
||||
if (childAssoc != null)
|
||||
{
|
||||
return childAssoc.getChild().getNodeRef();
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public ChildAssociationRef getPrimaryParent(NodeRef nodeRef) throws InvalidNodeRefException
|
||||
@@ -1041,11 +1112,10 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
}
|
||||
|
||||
public List<AssociationRef> getTargetAssocs(NodeRef sourceRef, QNamePattern qnamePattern)
|
||||
throws InvalidNodeRefException
|
||||
{
|
||||
Node sourceNode = getNodeNotNull(sourceRef);
|
||||
// get all assocs to target
|
||||
Collection<NodeAssoc> assocs = sourceNode.getTargetNodeAssocs();
|
||||
Collection<NodeAssoc> assocs = nodeDaoService.getTargetNodeAssocs(sourceNode);
|
||||
List<AssociationRef> nodeAssocRefs = new ArrayList<AssociationRef>(assocs.size());
|
||||
for (NodeAssoc assoc : assocs)
|
||||
{
|
||||
@@ -1061,11 +1131,10 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
}
|
||||
|
||||
public List<AssociationRef> getSourceAssocs(NodeRef targetRef, QNamePattern qnamePattern)
|
||||
throws InvalidNodeRefException
|
||||
{
|
||||
Node sourceNode = getNodeNotNull(targetRef);
|
||||
Node targetNode = getNodeNotNull(targetRef);
|
||||
// get all assocs to source
|
||||
Collection<NodeAssoc> assocs = sourceNode.getSourceNodeAssocs();
|
||||
Collection<NodeAssoc> assocs = nodeDaoService.getSourceNodeAssocs(targetNode);
|
||||
List<AssociationRef> nodeAssocRefs = new ArrayList<AssociationRef>(assocs.size());
|
||||
for (NodeAssoc assoc : assocs)
|
||||
{
|
||||
@@ -1367,7 +1436,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
// add the node to the map
|
||||
nodesById.put(id, node);
|
||||
// recurse into the primary children
|
||||
Collection<ChildAssoc> childAssocs = node.getChildAssocs();
|
||||
Collection<ChildAssoc> childAssocs = nodeDaoService.getChildAssocs(node);
|
||||
for (ChildAssoc childAssoc : childAssocs)
|
||||
{
|
||||
// cascade into primary associations
|
||||
@@ -1394,7 +1463,8 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
List<ChildAssoc> childAssocsToDelete = new ArrayList<ChildAssoc>(5);
|
||||
// child associations
|
||||
ArrayList<ChildAssociationRef> archivedChildAssocRefs = new ArrayList<ChildAssociationRef>(5);
|
||||
for (ChildAssoc assoc : node.getChildAssocs())
|
||||
Collection<ChildAssoc> childAssocs = nodeDaoService.getChildAssocs(node);
|
||||
for (ChildAssoc assoc : childAssocs)
|
||||
{
|
||||
Long relatedNodeId = assoc.getChild().getId();
|
||||
if (nodesById.containsKey(relatedNodeId))
|
||||
@@ -1427,7 +1497,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
List<NodeAssoc> nodeAssocsToDelete = new ArrayList<NodeAssoc>(5);
|
||||
// source associations
|
||||
ArrayList<AssociationRef> archivedSourceAssocRefs = new ArrayList<AssociationRef>(5);
|
||||
for (NodeAssoc assoc : node.getSourceNodeAssocs())
|
||||
for (NodeAssoc assoc : nodeDaoService.getSourceNodeAssocs(node))
|
||||
{
|
||||
Long relatedNodeId = assoc.getSource().getId();
|
||||
if (nodesById.containsKey(relatedNodeId))
|
||||
@@ -1440,7 +1510,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
}
|
||||
// target associations
|
||||
ArrayList<AssociationRef> archivedTargetAssocRefs = new ArrayList<AssociationRef>(5);
|
||||
for (NodeAssoc assoc : node.getTargetNodeAssocs())
|
||||
for (NodeAssoc assoc : nodeDaoService.getTargetNodeAssocs(node))
|
||||
{
|
||||
Long relatedNodeId = assoc.getTarget().getId();
|
||||
if (nodesById.containsKey(relatedNodeId))
|
||||
@@ -1599,10 +1669,21 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
continue;
|
||||
}
|
||||
Node parentNode = getNodeNotNull(parentNodeRef);
|
||||
nodeDaoService.newChildAssoc(parentNode, node, assocRef.isPrimary(), assocRef.getTypeQName(), assocRef.getQName());
|
||||
// get the name to use for the unique child check
|
||||
QName assocTypeQName = assocRef.getTypeQName();
|
||||
nodeDaoService.newChildAssoc(
|
||||
parentNode,
|
||||
node,
|
||||
assocRef.isPrimary(),
|
||||
assocTypeQName,
|
||||
assocRef.getQName());
|
||||
}
|
||||
properties.remove(ContentModel.PROP_ARCHIVED_PARENT_ASSOCS);
|
||||
}
|
||||
|
||||
// make sure that the node name uniqueness is enforced
|
||||
setChildUniqueName(node);
|
||||
|
||||
// restore child associations
|
||||
Collection<ChildAssociationRef> childAssocRefs = (Collection<ChildAssociationRef>) getProperty(
|
||||
nodeRef,
|
||||
@@ -1617,7 +1698,16 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
continue;
|
||||
}
|
||||
Node childNode = getNodeNotNull(childNodeRef);
|
||||
nodeDaoService.newChildAssoc(node, childNode, assocRef.isPrimary(), assocRef.getTypeQName(), assocRef.getQName());
|
||||
QName assocTypeQName = assocRef.getTypeQName();
|
||||
// get the name to use for the unique child check
|
||||
nodeDaoService.newChildAssoc(
|
||||
node,
|
||||
childNode,
|
||||
assocRef.isPrimary(),
|
||||
assocTypeQName,
|
||||
assocRef.getQName());
|
||||
// ensure that the name uniqueness is enforced for the child node
|
||||
setChildUniqueName(childNode);
|
||||
}
|
||||
properties.remove(ContentModel.PROP_ARCHIVED_CHILD_ASSOCS);
|
||||
}
|
||||
@@ -1660,4 +1750,56 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
// remove the aspect
|
||||
node.getAspects().remove(ContentModel.ASPECT_ARCHIVED_ASSOCS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the dictionary's definition of the association to assign a unique name to the child node.
|
||||
*
|
||||
* @param assocTypeQName the type of the child association
|
||||
* @param childNode the child node being added. The name will be extracted from it, if necessary.
|
||||
* @return Returns the value to be put on the child association for uniqueness, or null if
|
||||
*/
|
||||
private void setChildUniqueName(Node childNode)
|
||||
{
|
||||
// get the name property
|
||||
Map<QName, PropertyValue> properties = childNode.getProperties();
|
||||
PropertyValue nameValue = properties.get(ContentModel.PROP_NAME);
|
||||
String useName = null;
|
||||
if (nameValue == null)
|
||||
{
|
||||
// no name has been assigned, so assign the ID of the child node
|
||||
useName = childNode.getUuid();
|
||||
}
|
||||
else
|
||||
{
|
||||
useName = (String) nameValue.getValue(DataTypeDefinition.TEXT);
|
||||
}
|
||||
// get all the parent assocs
|
||||
Collection<ChildAssoc> parentAssocs = childNode.getParentAssocs();
|
||||
for (ChildAssoc assoc : parentAssocs)
|
||||
{
|
||||
QName assocTypeQName = assoc.getTypeQName();
|
||||
AssociationDefinition assocDef = dictionaryService.getAssociation(assocTypeQName);
|
||||
if (!assocDef.isChild())
|
||||
{
|
||||
throw new DataIntegrityViolationException("Child association has non-child type: " + assoc.getId());
|
||||
}
|
||||
ChildAssociationDefinition childAssocDef = (ChildAssociationDefinition) assocDef;
|
||||
if (childAssocDef.getDuplicateChildNamesAllowed())
|
||||
{
|
||||
// the name is irrelevant, so it doesn't need to be put into the unique key
|
||||
nodeDaoService.setChildNameUnique(assoc, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
nodeDaoService.setChildNameUnique(assoc, useName);
|
||||
}
|
||||
}
|
||||
// done
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug(
|
||||
"Unique name set for all " + parentAssocs.size() + " parent associations: \n" +
|
||||
" name: " + useName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
package org.alfresco.repo.node.db;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.alfresco.repo.domain.ChildAssoc;
|
||||
@@ -24,6 +25,7 @@ import org.alfresco.repo.domain.NodeAssoc;
|
||||
import org.alfresco.repo.domain.NodeStatus;
|
||||
import org.alfresco.repo.domain.Store;
|
||||
import org.alfresco.service.cmr.dictionary.InvalidTypeException;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
@@ -128,6 +130,30 @@ public interface NodeDaoService
|
||||
QName assocTypeQName,
|
||||
QName qname);
|
||||
|
||||
/**
|
||||
* Change the name of the child node.
|
||||
*
|
||||
* @param childAssoc the child association to change
|
||||
* @param childName the name to put on the association
|
||||
*/
|
||||
public void setChildNameUnique(ChildAssoc childAssoc, String childName);
|
||||
|
||||
/**
|
||||
* Get all child associations for a given node
|
||||
*
|
||||
* @param parentNode the parent of the child associations
|
||||
* @return Returns all child associations for the given node
|
||||
*/
|
||||
public Collection<ChildAssoc> getChildAssocs(final Node parentNode);
|
||||
|
||||
/**
|
||||
* Get a collection of all child association references for a given parent node.
|
||||
*
|
||||
* @param parentNode the parent node
|
||||
* @return Returns a collection of association references
|
||||
*/
|
||||
public Collection<ChildAssociationRef> getChildAssocRefs(Node parentNode);
|
||||
|
||||
/**
|
||||
* @return Returns a matching association or null if one was not found
|
||||
*
|
||||
@@ -138,7 +164,12 @@ public interface NodeDaoService
|
||||
Node childNode,
|
||||
QName assocTypeQName,
|
||||
QName qname);
|
||||
|
||||
|
||||
/**
|
||||
* @return Returns an association matching the given parent, type and child name - or null
|
||||
* if not found
|
||||
*/
|
||||
public ChildAssoc getChildAssoc(Node parentNode, QName assocTypeQName, String childName);
|
||||
|
||||
/**
|
||||
* @param assoc the child association to remove
|
||||
@@ -164,6 +195,11 @@ public interface NodeDaoService
|
||||
Node targetNode,
|
||||
QName assocTypeQName);
|
||||
|
||||
/**
|
||||
* @return Returns a list of all node associations associated with the given node
|
||||
*/
|
||||
public List<NodeAssoc> getNodeAssocsToAndFrom(final Node node);
|
||||
|
||||
/**
|
||||
* @return Returns the node association or null if not found
|
||||
*/
|
||||
@@ -172,6 +208,16 @@ public interface NodeDaoService
|
||||
Node targetNode,
|
||||
QName assocTypeQName);
|
||||
|
||||
/**
|
||||
* @return Returns all the node associations where the node is the <b>source</b>
|
||||
*/
|
||||
public List<NodeAssoc> getTargetNodeAssocs(Node sourceNode);
|
||||
|
||||
/**
|
||||
* @return Returns all the node associations where the node is the </b>target</b>
|
||||
*/
|
||||
public List<NodeAssoc> getSourceNodeAssocs(Node targetNode);
|
||||
|
||||
/**
|
||||
* @param assoc the node association to remove
|
||||
*/
|
||||
|
@@ -19,7 +19,9 @@ package org.alfresco.repo.node.db.hibernate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.zip.CRC32;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.domain.ChildAssoc;
|
||||
import org.alfresco.repo.domain.Node;
|
||||
@@ -37,9 +39,11 @@ import org.alfresco.repo.node.db.NodeDaoService;
|
||||
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
||||
import org.alfresco.repo.transaction.TransactionalDao;
|
||||
import org.alfresco.service.cmr.dictionary.InvalidTypeException;
|
||||
import org.alfresco.service.cmr.repository.AssociationRef;
|
||||
import org.alfresco.service.cmr.repository.AssociationExistsException;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.DuplicateChildNodeNameException;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.util.GUID;
|
||||
import org.hibernate.ObjectDeletedException;
|
||||
@@ -58,6 +62,14 @@ import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
|
||||
public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements NodeDaoService, TransactionalDao
|
||||
{
|
||||
private static final String QUERY_GET_ALL_STORES = "store.GetAllStores";
|
||||
private static final String UPDATE_SET_CHILD_ASSOC_NAME = "node.updateChildAssocName";
|
||||
private static final String QUERY_GET_CHILD_ASSOCS = "node.GetChildAssocs";
|
||||
private static final String QUERY_GET_CHILD_ASSOC_BY_TYPE_AND_NAME = "node.GetChildAssocByTypeAndName";
|
||||
private static final String QUERY_GET_CHILD_ASSOC_REFS = "node.GetChildAssocRefs";
|
||||
private static final String QUERY_GET_NODE_ASSOC = "node.GetNodeAssoc";
|
||||
private static final String QUERY_GET_NODE_ASSOCS_TO_AND_FROM = "node.GetNodeAssocsToAndFrom";
|
||||
private static final String QUERY_GET_TARGET_ASSOCS = "node.GetTargetAssocs";
|
||||
private static final String QUERY_GET_SOURCE_ASSOCS = "node.GetSourceAssocs";
|
||||
private static final String QUERY_GET_CONTENT_DATA_STRINGS = "node.GetContentDataStrings";
|
||||
|
||||
/** a uuid identifying this unique instance */
|
||||
@@ -303,25 +315,17 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
|
||||
deleteChildAssoc(assoc, false); // we don't cascade upwards
|
||||
}
|
||||
// delete all child assocs
|
||||
Collection<ChildAssoc> childAssocs = node.getChildAssocs();
|
||||
Collection<ChildAssoc> childAssocs = getChildAssocs(node);
|
||||
childAssocs = new ArrayList<ChildAssoc>(childAssocs);
|
||||
for (ChildAssoc assoc : childAssocs)
|
||||
{
|
||||
deleteChildAssoc(assoc, cascade); // potentially cascade downwards
|
||||
}
|
||||
// delete all target assocs
|
||||
Collection<NodeAssoc> targetAssocs = node.getTargetNodeAssocs();
|
||||
targetAssocs = new ArrayList<NodeAssoc>(targetAssocs);
|
||||
for (NodeAssoc assoc : targetAssocs)
|
||||
// delete all node associations to and from
|
||||
List<NodeAssoc> nodeAssocs = getNodeAssocsToAndFrom(node);
|
||||
for (NodeAssoc assoc : nodeAssocs)
|
||||
{
|
||||
deleteNodeAssoc(assoc);
|
||||
}
|
||||
// delete all source assocs
|
||||
Collection<NodeAssoc> sourceAssocs = node.getSourceNodeAssocs();
|
||||
sourceAssocs = new ArrayList<NodeAssoc>(sourceAssocs);
|
||||
for (NodeAssoc assoc : sourceAssocs)
|
||||
{
|
||||
deleteNodeAssoc(assoc);
|
||||
getHibernateTemplate().delete(assoc);
|
||||
}
|
||||
// update the node status
|
||||
NodeRef nodeRef = node.getNodeRef();
|
||||
@@ -330,9 +334,34 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
|
||||
nodeStatus.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId());
|
||||
// finally delete the node
|
||||
getHibernateTemplate().delete(node);
|
||||
// flush to ensure constraints can't be violated
|
||||
getSession().flush();
|
||||
// done
|
||||
}
|
||||
|
||||
|
||||
private long getCrc(String str)
|
||||
{
|
||||
CRC32 crc = new CRC32();
|
||||
crc.update(str.getBytes());
|
||||
return crc.getValue();
|
||||
}
|
||||
|
||||
private static final String TRUNCATED_NAME_INDICATOR = "~~~";
|
||||
private String getShortName(String str)
|
||||
{
|
||||
int length = str.length();
|
||||
if (length <= 50)
|
||||
{
|
||||
return str;
|
||||
}
|
||||
else
|
||||
{
|
||||
StringBuilder ret = new StringBuilder(50);
|
||||
ret.append(str.substring(0, 47)).append(TRUNCATED_NAME_INDICATOR);
|
||||
return ret.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public ChildAssoc newChildAssoc(
|
||||
Node parentNode,
|
||||
Node childNode,
|
||||
@@ -340,17 +369,170 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
|
||||
QName assocTypeQName,
|
||||
QName qname)
|
||||
{
|
||||
/*
|
||||
* This initial child association creation will fail IFF there is already
|
||||
* an association of the given type between the two nodes. For new association
|
||||
* creation, this can only occur if two transactions attempt to create a secondary
|
||||
* child association between the same two nodes. As this is unlikely, it is
|
||||
* appropriate to just throw a runtime exception and let the second transaction
|
||||
* fail.
|
||||
*
|
||||
* We don't need to flush the session beforehand as there won't be any deletions
|
||||
* of the assocation pending. The association deletes, on the other hand, have
|
||||
* to flush early in order to ensure that the database unique index isn't violated
|
||||
* if the association is recreated subsequently.
|
||||
*/
|
||||
|
||||
String uuid = childNode.getUuid();
|
||||
|
||||
ChildAssoc assoc = new ChildAssocImpl();
|
||||
assoc.setTypeQName(assocTypeQName);
|
||||
assoc.setIsPrimary(isPrimary);
|
||||
assoc.setChildNodeName(getShortName(uuid));
|
||||
assoc.setChildNodeNameCrc(getCrc(uuid));
|
||||
assoc.setQname(qname);
|
||||
assoc.setIsPrimary(isPrimary);
|
||||
assoc.buildAssociation(parentNode, childNode);
|
||||
// persist
|
||||
getHibernateTemplate().save(assoc);
|
||||
// persist it, catching the duplicate child name
|
||||
try
|
||||
{
|
||||
getHibernateTemplate().save(assoc);
|
||||
}
|
||||
catch (DataIntegrityViolationException e)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("An association already exists between the two nodes: \n" +
|
||||
" parent: " + parentNode.getId() + "\n" +
|
||||
" child: " + childNode.getId() + "\n" +
|
||||
" assoc: " + assocTypeQName,
|
||||
e);
|
||||
}
|
||||
// done
|
||||
return assoc;
|
||||
}
|
||||
|
||||
public void setChildNameUnique(final ChildAssoc childAssoc, String childName)
|
||||
{
|
||||
/*
|
||||
* As the Hibernate session is rendered useless when an exception is
|
||||
* bubbled up, we go direct to the database to update the child association.
|
||||
* This preserves the session and client code can catch the resulting
|
||||
* exception and react to it whilst in the same transaction.
|
||||
*
|
||||
* We ensure that case-insensitivity is maintained by persisting
|
||||
* the lowercase version of the child node name.
|
||||
*/
|
||||
|
||||
String childNameNew = null;
|
||||
if (childName == null)
|
||||
{
|
||||
childNameNew = childAssoc.getChild().getUuid();
|
||||
}
|
||||
else
|
||||
{
|
||||
childNameNew = childName.toLowerCase();
|
||||
}
|
||||
|
||||
final String childNameNewShort = getShortName(childNameNew);
|
||||
final long childNameNewCrc = getCrc(childNameNew);
|
||||
|
||||
// check if the name has changed
|
||||
if (childAssoc.getChildNodeNameCrc() == childNameNewCrc)
|
||||
{
|
||||
if (childAssoc.getChildNodeName().equals(childNameNewShort))
|
||||
{
|
||||
// nothing changed
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
HibernateCallback callback = new HibernateCallback()
|
||||
{
|
||||
public Object doInHibernate(Session session)
|
||||
{
|
||||
session.flush();
|
||||
Query query = session
|
||||
.getNamedQuery(HibernateNodeDaoServiceImpl.UPDATE_SET_CHILD_ASSOC_NAME)
|
||||
.setString("newName", childNameNewShort)
|
||||
.setLong("newNameCrc", childNameNewCrc)
|
||||
.setLong("childAssocId", childAssoc.getId());
|
||||
return (Integer) query.executeUpdate();
|
||||
}
|
||||
};
|
||||
try
|
||||
{
|
||||
Integer count = (Integer) getHibernateTemplate().execute(callback);
|
||||
// refresh the entity directly
|
||||
getHibernateTemplate().refresh(childAssoc);
|
||||
if (count.intValue() == 0)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("ChildAssoc not updated: " + childAssoc.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (DataIntegrityViolationException e)
|
||||
{
|
||||
NodeRef parentNodeRef = childAssoc.getParent().getNodeRef();
|
||||
QName assocTypeQName = childAssoc.getTypeQName();
|
||||
throw new DuplicateChildNodeNameException(parentNodeRef, assocTypeQName, childName);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Collection<ChildAssoc> getChildAssocs(final Node parentNode)
|
||||
{
|
||||
HibernateCallback callback = new HibernateCallback()
|
||||
{
|
||||
public Object doInHibernate(Session session)
|
||||
{
|
||||
Query query = session
|
||||
.getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_CHILD_ASSOCS)
|
||||
.setLong("parentId", parentNode.getId());
|
||||
return query.list();
|
||||
}
|
||||
};
|
||||
List<ChildAssoc> queryResults = (List) getHibernateTemplate().execute(callback);
|
||||
return queryResults;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Collection<ChildAssociationRef> getChildAssocRefs(final Node parentNode)
|
||||
{
|
||||
HibernateCallback callback = new HibernateCallback()
|
||||
{
|
||||
public Object doInHibernate(Session session)
|
||||
{
|
||||
Query query = session
|
||||
.getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_CHILD_ASSOC_REFS)
|
||||
.setLong("parentId", parentNode.getId());
|
||||
return query.list();
|
||||
}
|
||||
};
|
||||
List<Object[]> queryResults = (List<Object[]>) getHibernateTemplate().execute(callback);
|
||||
Collection<ChildAssociationRef> refs = new ArrayList<ChildAssociationRef>(queryResults.size());
|
||||
NodeRef parentNodeRef = parentNode.getNodeRef();
|
||||
for (Object[] row : queryResults)
|
||||
{
|
||||
String childProtocol = (String) row[5];
|
||||
String childIdentifier = (String) row[6];
|
||||
String childUuid = (String) row[7];
|
||||
NodeRef childNodeRef = new NodeRef(new StoreRef(childProtocol, childIdentifier), childUuid);
|
||||
QName assocTypeQName = (QName) row[0];
|
||||
QName assocQName = (QName) row[1];
|
||||
Boolean assocIsPrimary = (Boolean) row[2];
|
||||
Integer assocIndex = (Integer) row[3];
|
||||
ChildAssociationRef assocRef = new ChildAssociationRef(
|
||||
assocTypeQName,
|
||||
parentNodeRef,
|
||||
assocQName,
|
||||
childNodeRef,
|
||||
assocIsPrimary.booleanValue(),
|
||||
assocIndex.intValue());
|
||||
refs.add(assocRef);
|
||||
}
|
||||
return refs;
|
||||
}
|
||||
|
||||
public ChildAssoc getChildAssoc(
|
||||
Node parentNode,
|
||||
Node childNode,
|
||||
@@ -363,7 +545,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
|
||||
qname,
|
||||
childNode.getNodeRef());
|
||||
// get all the parent's child associations
|
||||
Collection<ChildAssoc> assocs = parentNode.getChildAssocs();
|
||||
Collection<ChildAssoc> assocs = getChildAssocs(parentNode);
|
||||
// hunt down the desired assoc
|
||||
for (ChildAssoc assoc : assocs)
|
||||
{
|
||||
@@ -381,6 +563,28 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
|
||||
return null;
|
||||
}
|
||||
|
||||
public ChildAssoc getChildAssoc(final Node parentNode, final QName assocTypeQName, final String childName)
|
||||
{
|
||||
HibernateCallback callback = new HibernateCallback()
|
||||
{
|
||||
public Object doInHibernate(Session session)
|
||||
{
|
||||
String childNameLower = childName.toLowerCase();
|
||||
String childNameShort = getShortName(childNameLower);
|
||||
long childNameLowerCrc = getCrc(childNameLower);
|
||||
Query query = session
|
||||
.getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_CHILD_ASSOC_BY_TYPE_AND_NAME)
|
||||
.setLong("parentId", parentNode.getId())
|
||||
.setParameter("typeQName", assocTypeQName)
|
||||
.setParameter("childNodeName", childNameShort)
|
||||
.setLong("childNodeNameCrc", childNameLowerCrc);
|
||||
return query.uniqueResult();
|
||||
}
|
||||
};
|
||||
ChildAssoc childAssoc = (ChildAssoc) getHibernateTemplate().execute(callback);
|
||||
return childAssoc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually enforces cascade deletions down primary associations
|
||||
*/
|
||||
@@ -403,6 +607,10 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
|
||||
* duplicate call will be received to do this
|
||||
*/
|
||||
}
|
||||
|
||||
// To ensure the validity of the constraint enforcement by the database,
|
||||
// we have to flush here
|
||||
getSession().flush();
|
||||
}
|
||||
|
||||
public ChildAssoc getPrimaryParentAssoc(Node node)
|
||||
@@ -458,45 +666,101 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
|
||||
assoc.setTypeQName(assocTypeQName);
|
||||
assoc.buildAssociation(sourceNode, targetNode);
|
||||
// persist
|
||||
getHibernateTemplate().save(assoc);
|
||||
try
|
||||
{
|
||||
getHibernateTemplate().save(assoc);
|
||||
}
|
||||
catch (DataIntegrityViolationException e)
|
||||
{
|
||||
throw new AssociationExistsException(
|
||||
sourceNode.getNodeRef(),
|
||||
targetNode.getNodeRef(),
|
||||
assocTypeQName,
|
||||
e);
|
||||
}
|
||||
// done
|
||||
return assoc;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<NodeAssoc> getNodeAssocsToAndFrom(final Node node)
|
||||
{
|
||||
HibernateCallback callback = new HibernateCallback()
|
||||
{
|
||||
public Object doInHibernate(Session session)
|
||||
{
|
||||
Query query = session
|
||||
.getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_NODE_ASSOCS_TO_AND_FROM)
|
||||
.setLong("nodeId", node.getId());
|
||||
return query.list();
|
||||
}
|
||||
};
|
||||
List<NodeAssoc> results = (List<NodeAssoc>) getHibernateTemplate().execute(callback);
|
||||
return results;
|
||||
}
|
||||
|
||||
public NodeAssoc getNodeAssoc(
|
||||
final Node sourceNode,
|
||||
final Node targetNode,
|
||||
final QName assocTypeQName)
|
||||
{
|
||||
AssociationRef nodeAssocRef = new AssociationRef(
|
||||
sourceNode.getNodeRef(),
|
||||
assocTypeQName,
|
||||
targetNode.getNodeRef());
|
||||
// get all the source's target associations
|
||||
Collection<NodeAssoc> assocs = sourceNode.getTargetNodeAssocs();
|
||||
// hunt down the desired assoc
|
||||
for (NodeAssoc assoc : assocs)
|
||||
HibernateCallback callback = new HibernateCallback()
|
||||
{
|
||||
// is it a match?
|
||||
if (!assoc.getNodeAssocRef().equals(nodeAssocRef)) // not a match
|
||||
public Object doInHibernate(Session session)
|
||||
{
|
||||
continue;
|
||||
Query query = session
|
||||
.getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_NODE_ASSOC)
|
||||
.setLong("sourceId", sourceNode.getId())
|
||||
.setLong("targetId", targetNode.getId())
|
||||
.setParameter("assocTypeQName", assocTypeQName);
|
||||
return query.uniqueResult();
|
||||
}
|
||||
else
|
||||
};
|
||||
NodeAssoc result = (NodeAssoc) getHibernateTemplate().execute(callback);
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<NodeAssoc> getTargetNodeAssocs(final Node sourceNode)
|
||||
{
|
||||
HibernateCallback callback = new HibernateCallback()
|
||||
{
|
||||
public Object doInHibernate(Session session)
|
||||
{
|
||||
return assoc;
|
||||
Query query = session
|
||||
.getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_TARGET_ASSOCS)
|
||||
.setLong("sourceId", sourceNode.getId());
|
||||
return query.list();
|
||||
}
|
||||
}
|
||||
// not found
|
||||
return null;
|
||||
};
|
||||
List<NodeAssoc> queryResults = (List<NodeAssoc>) getHibernateTemplate().execute(callback);
|
||||
return queryResults;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<NodeAssoc> getSourceNodeAssocs(final Node targetNode)
|
||||
{
|
||||
HibernateCallback callback = new HibernateCallback()
|
||||
{
|
||||
public Object doInHibernate(Session session)
|
||||
{
|
||||
Query query = session
|
||||
.getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_SOURCE_ASSOCS)
|
||||
.setLong("targetId", targetNode.getId());
|
||||
return query.list();
|
||||
}
|
||||
};
|
||||
List<NodeAssoc> queryResults = (List<NodeAssoc>) getHibernateTemplate().execute(callback);
|
||||
return queryResults;
|
||||
}
|
||||
|
||||
public void deleteNodeAssoc(NodeAssoc assoc)
|
||||
{
|
||||
// maintain inverse association sets
|
||||
assoc.removeAssociation();
|
||||
// remove instance
|
||||
// Remove instance
|
||||
getHibernateTemplate().delete(assoc);
|
||||
// Flush to ensure that the database constraints aren't violated if the assoc
|
||||
// is recreated in the transaction
|
||||
getSession().flush();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -513,24 +777,4 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
|
||||
List<String> queryResults = (List) getHibernateTemplate().execute(callback);
|
||||
return queryResults;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@@ -96,7 +96,7 @@ public class NodeIndexer
|
||||
indexer.updateNode(nodeRef);
|
||||
}
|
||||
|
||||
public void onDeleteNode(ChildAssociationRef childAssocRef)
|
||||
public void onDeleteNode(ChildAssociationRef childAssocRef, boolean isArchivedNode)
|
||||
{
|
||||
indexer.deleteNode(childAssocRef);
|
||||
}
|
||||
|
@@ -145,7 +145,7 @@ public class AssocSourceMultiplicityIntegrityEvent extends AbstractIntegrityEven
|
||||
}
|
||||
if ((mandatory && actualSize == 0) || (!allowMany && actualSize > 1))
|
||||
{
|
||||
String parentOrSourceStr = (assocDef.isChild() ? "child" : "target");
|
||||
String parentOrSourceStr = (assocDef.isChild() ? "parent" : "source");
|
||||
IntegrityRecord result = new IntegrityRecord(
|
||||
"The association " + parentOrSourceStr + " multiplicity has been violated: \n" +
|
||||
" Association: " + assocDef + "\n" +
|
||||
|
@@ -21,14 +21,10 @@ import java.util.List;
|
||||
import org.alfresco.service.cmr.dictionary.AssociationDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
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.service.namespace.RegexQNamePattern;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Event to check the association target role name
|
||||
@@ -37,8 +33,6 @@ import org.apache.commons.logging.LogFactory;
|
||||
*/
|
||||
public class AssocTargetRoleIntegrityEvent extends AbstractIntegrityEvent
|
||||
{
|
||||
private static Log logger = LogFactory.getLog(AssocTargetRoleIntegrityEvent.class);
|
||||
|
||||
public AssocTargetRoleIntegrityEvent(
|
||||
NodeService nodeService,
|
||||
DictionaryService dictionaryService,
|
||||
@@ -51,7 +45,6 @@ public class AssocTargetRoleIntegrityEvent extends AbstractIntegrityEvent
|
||||
|
||||
public void checkIntegrity(List<IntegrityRecord> eventResults)
|
||||
{
|
||||
NodeRef sourceNodeRef = getNodeRef();
|
||||
QName assocTypeQName = getTypeQName();
|
||||
QName assocQName = getQName();
|
||||
|
||||
@@ -80,7 +73,6 @@ public class AssocTargetRoleIntegrityEvent extends AbstractIntegrityEvent
|
||||
|
||||
// perform required checks
|
||||
checkAssocQNameRegex(eventResults, childAssocDef, assocQName);
|
||||
checkAssocQNameDuplicate(eventResults, childAssocDef, sourceNodeRef, assocQName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,39 +100,4 @@ public class AssocTargetRoleIntegrityEvent extends AbstractIntegrityEvent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the association name matches the constraints imposed by the model.
|
||||
*/
|
||||
protected void checkAssocQNameDuplicate(
|
||||
List<IntegrityRecord> eventResults,
|
||||
ChildAssociationDefinition assocDef,
|
||||
NodeRef sourceNodeRef,
|
||||
QName assocQName)
|
||||
{
|
||||
if (assocDef.getDuplicateChildNamesAllowed())
|
||||
{
|
||||
// nothing to do
|
||||
return;
|
||||
}
|
||||
QName assocTypeQName = assocDef.getName();
|
||||
// see if there is another association with the same name
|
||||
try
|
||||
{
|
||||
List<ChildAssociationRef> childAssocs = nodeService.getChildAssocs(sourceNodeRef, assocTypeQName, assocQName);
|
||||
// duplicates not allowed
|
||||
if (childAssocs.size() > 1)
|
||||
{
|
||||
IntegrityRecord result = new IntegrityRecord(
|
||||
"Duplicate child associations are not allowed: \n" +
|
||||
" Association: " + assocDef + "\n" +
|
||||
" Name: " + assocQName);
|
||||
eventResults.add(result);
|
||||
}
|
||||
}
|
||||
catch (InvalidNodeRefException e)
|
||||
{
|
||||
// node has gone
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -335,7 +335,7 @@ public class IntegrityChecker
|
||||
/**
|
||||
* No checking performed: The association changes will be handled
|
||||
*/
|
||||
public void onDeleteNode(ChildAssociationRef childAssocRef)
|
||||
public void onDeleteNode(ChildAssociationRef childAssocRef, boolean isArchivedNode)
|
||||
{
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user