From 4165e470325ae61b6ecff82e6ea51a8795cd8666 Mon Sep 17 00:00:00 2001
From: Alan Davis
Date: Fri, 30 Oct 2015 00:10:30 +0000
Subject: [PATCH] Merged 5.1-MNT1 (5.1.0) to HEAD (5.1) 115460 adavis:
Merged 5.1.N (5.1.1) to 5.1-MNT1 (5.1.0) 113727 amorarasu: Merged 5.0.N
(5.0.3) to 5.1.N (5.1.1) 113684 adavis: Merged V4.2-BUG-FIX (4.2.6)
to 5.0.N (5.0.3) (PARTIAL MERGE) 113603 cturlica: Merged DEV to
V4.2-BUG-FIX (4.2.6) 113602 cturlica: MNT-14504: Cloud pull
process not working after large delete
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@115670 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
---
.../node-common-SqlMap.xml | 16 +++-
.../repo/domain/node/AbstractNodeDAOImpl.java | 62 +++++++++++++++
.../repo/domain/node/NodeAssocEntity.java | 27 ++++++-
.../alfresco/repo/domain/node/NodeDAO.java | 11 +++
.../repo/domain/node/ibatis/NodeDAOImpl.java | 18 ++++-
.../repo/node/db/DbNodeServiceImpl.java | 35 +++++++++
.../repo/version/Node2ServiceImpl.java | 53 +++++++++++++
.../repo/version/NodeServiceImpl.java | 12 +++
.../repo/node/BaseNodeServiceTest.java | 75 ++++++++++++++++++-
.../repo/version/NodeServiceImplTest.java | 26 ++++++-
10 files changed, 327 insertions(+), 8 deletions(-)
diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-common-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-common-SqlMap.xml
index c14e31db70..2688c25f8a 100644
--- a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-common-SqlMap.xml
+++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-common-SqlMap.xml
@@ -823,10 +823,24 @@
targetNode.id = #{id}
-
+
+
+
+ join alf_node_properties prop on (targetNode.id = prop.node_id)
+
where
sourceNode.id = #{sourceNode.id}
+
+
+ and prop.qname_id = #{propertyQNameId}
+
+ and prop.string_value = #{propertyValue.stringValue}
+ and prop.double_value = #{propertyValue.doubleValue}
+ and prop.long_value = #{propertyValue.longValue}
+ and prop.boolean_value = #{propertyValue.booleanValue}
+
+
diff --git a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java
index d1d4a19820..559efe2bcc 100644
--- a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java
+++ b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java
@@ -3003,6 +3003,67 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
return results;
}
+ @Override
+ public Collection> getTargetAssocsByPropertyValue(Long sourceNodeId, QName typeQName, QName propertyQName, Serializable propertyValue)
+ {
+ Long typeQNameId = null;
+ if (typeQName != null)
+ {
+ Pair typeQNamePair = qnameDAO.getQName(typeQName);
+ if (typeQNamePair == null)
+ {
+ // No such QName
+ return Collections.emptyList();
+ }
+ typeQNameId = typeQNamePair.getFirst();
+ }
+
+ Long propertyQNameId = null;
+ NodePropertyValue nodeValue = null;
+ if (propertyQName != null)
+ {
+
+ Pair propQNamePair = qnameDAO.getQName(propertyQName);
+ if (propQNamePair == null)
+ {
+ // No such QName
+ return Collections.emptyList();
+ }
+
+ propertyQNameId = propQNamePair.getFirst();
+
+ PropertyDefinition propertyDef = dictionaryService.getProperty(propertyQName);
+
+ nodeValue = nodePropertyHelper.makeNodePropertyValue(propertyDef, propertyValue);
+ if (nodeValue != null)
+ {
+ switch (nodeValue.getPersistedType())
+ {
+ case 1: // Boolean
+ case 3: // long
+ case 5: // double
+ case 6: // string
+ // no floats due to the range errors testing equality on a float.
+ break;
+ default:
+ throw new IllegalArgumentException("method not supported for persisted value type " + nodeValue.getPersistedType());
+ }
+ }
+ }
+
+ List nodeAssocEntities = selectNodeAssocsBySourceAndPropertyValue(sourceNodeId, typeQNameId, propertyQNameId, nodeValue);
+
+ // Create custom result
+ List> results = new ArrayList>(nodeAssocEntities.size());
+ for (NodeAssocEntity nodeAssocEntity : nodeAssocEntities)
+ {
+ Long assocId = nodeAssocEntity.getId();
+ AssociationRef assocRef = nodeAssocEntity.getAssociationRef(qnameDAO);
+ results.add(new Pair(assocId, assocRef));
+ }
+ return results;
+ }
+
@Override
public Pair getNodeAssocOrNull(Long assocId)
{
@@ -4871,6 +4932,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
protected abstract int deleteNodeAssocs(List ids);
protected abstract List selectNodeAssocs(Long nodeId);
protected abstract List selectNodeAssocsBySource(Long sourceNodeId, Long typeQNameId);
+ protected abstract List selectNodeAssocsBySourceAndPropertyValue(Long sourceNodeId, Long typeQNameId, Long propertyQNameId, NodePropertyValue nodeValue);
protected abstract List selectNodeAssocsByTarget(Long targetNodeId, Long typeQNameId);
protected abstract NodeAssocEntity selectNodeAssocById(Long assocId);
protected abstract int selectNodeAssocMaxIndex(Long sourceNodeId, Long assocTypeQNameId);
diff --git a/source/java/org/alfresco/repo/domain/node/NodeAssocEntity.java b/source/java/org/alfresco/repo/domain/node/NodeAssocEntity.java
index a78c449125..8f4ed8beb6 100644
--- a/source/java/org/alfresco/repo/domain/node/NodeAssocEntity.java
+++ b/source/java/org/alfresco/repo/domain/node/NodeAssocEntity.java
@@ -39,7 +39,11 @@ public class NodeAssocEntity
private Long typeQNameId;
private int assocIndex;
private List typeQNameIds;
-
+
+ // Supplemental query-related parameters
+ private Long propertyQNameId;
+ private NodePropertyValue propertyValue;
+
/**
* Required default constructor
*/
@@ -145,4 +149,25 @@ public class NodeAssocEntity
{
this.typeQNameIds = typeQNameIds;
}
+
+ public Long getPropertyQNameId()
+ {
+ return propertyQNameId;
+ }
+
+ public void setPropertyQNameId(Long propertyQNameId)
+ {
+ this.propertyQNameId = propertyQNameId;
+ }
+
+ public NodePropertyValue getPropertyValue()
+ {
+ return propertyValue;
+ }
+
+ public void setPropertyValue(NodePropertyValue propertyValue)
+ {
+ this.propertyValue = propertyValue;
+ }
+
}
diff --git a/source/java/org/alfresco/repo/domain/node/NodeDAO.java b/source/java/org/alfresco/repo/domain/node/NodeDAO.java
index d0112883c2..4ea513d328 100644
--- a/source/java/org/alfresco/repo/domain/node/NodeDAO.java
+++ b/source/java/org/alfresco/repo/domain/node/NodeDAO.java
@@ -457,6 +457,17 @@ public interface NodeDAO extends NodeBulkLoader
*/
public Collection> getTargetNodeAssocs(Long sourceNodeId, QName typeQName);
+ /**
+ * Get target associations by type of the association, property name and value.
+ *
+ * @param sourceNodeId the source of the association
+ * @param typeQName the type of the association (null allowed)
+ * @param propertyQName property QName (null allowed)
+ * @param propertyValue property value (null allowed only if the propertyQName is null )
+ * @return Returns all the node associations where the node is the source .
+ */
+ public Collection> getTargetAssocsByPropertyValue(Long sourceNodeId, QName typeQName, QName propertyQName, Serializable propertyValue);
+
/**
* @return Returns a specific node association with the given ID
* or null if it doesn't exist
diff --git a/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java b/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java
index 2439c5b70d..4172431835 100644
--- a/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java
+++ b/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java
@@ -109,7 +109,7 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
private static final String DELETE_NODE_ASSOC = "alfresco.node.delete_NodeAssoc";
private static final String DELETE_NODE_ASSOCS = "alfresco.node.delete_NodeAssocs";
private static final String SELECT_NODE_ASSOCS = "alfresco.node.select_NodeAssocs";
- private static final String SELECT_NODE_ASSOCS_BY_SOURCE = "alfresco.node.select_NodeAssocsBySource";
+ private static final String SELECT_NODE_ASSOCS_BY_SOURCE_AND_PROPERTY_VALUE = "alfresco.node.select_NodeAssocsBySourceAndPropertyValue";
private static final String SELECT_NODE_ASSOCS_BY_TARGET = "alfresco.node.select_NodeAssocsByTarget";
private static final String SELECT_NODE_ASSOC_BY_ID = "alfresco.node.select_NodeAssocById";
private static final String SELECT_NODE_ASSOCS_MAX_INDEX = "alfresco.node.select_NodeAssocsMaxId";
@@ -754,9 +754,15 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
return template.selectList(SELECT_NODE_ASSOCS, node);
}
- @SuppressWarnings("unchecked")
@Override
protected List selectNodeAssocsBySource(Long sourceNodeId, Long typeQNameId)
+ {
+ return selectNodeAssocsBySourceAndPropertyValue(sourceNodeId, typeQNameId, null, null);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected List selectNodeAssocsBySourceAndPropertyValue(Long sourceNodeId, Long typeQNameId, Long propertyQNameId, NodePropertyValue nodeValue)
{
NodeAssocEntity assoc = new NodeAssocEntity();
// Source
@@ -765,8 +771,12 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
assoc.setSourceNode(sourceNode);
// Type
assoc.setTypeQNameId(typeQNameId);
-
- return template.selectList(SELECT_NODE_ASSOCS_BY_SOURCE, assoc);
+
+ // Property
+ assoc.setPropertyQNameId(propertyQNameId);
+ assoc.setPropertyValue(nodeValue);
+
+ return (List) template.selectList(SELECT_NODE_ASSOCS_BY_SOURCE_AND_PROPERTY_VALUE, assoc);
}
@SuppressWarnings("unchecked")
diff --git a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java
index abb0a99c04..51d1575e09 100644
--- a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java
+++ b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java
@@ -2238,6 +2238,41 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
return nodeAssocRefs;
}
+ @Override
+ public List getTargetAssocsByPropertyValue(NodeRef sourceRef, QNamePattern qnamePattern, QName propertyQName, Serializable propertyValue)
+ {
+ Pair sourceNodePair = getNodePairNotNull(sourceRef);
+ Long sourceNodeId = sourceNodePair.getFirst();
+
+ QName qnameFilter = null;
+ if (qnamePattern instanceof QName)
+ {
+ qnameFilter = (QName) qnamePattern;
+ }
+
+ // Check the QName is not one of the "special" system maintained ones.
+ if (getChildAssocsByPropertyValueBannedProps.contains(propertyQName))
+ {
+ throw new IllegalArgumentException(
+ "getTargetAssocsByPropertyValue does not allow search of system maintained properties: " + propertyQName);
+ }
+
+ Collection> assocPairs = nodeDAO.getTargetAssocsByPropertyValue(sourceNodeId, qnameFilter, propertyQName, propertyValue);
+ List nodeAssocRefs = new ArrayList(assocPairs.size());
+ for (Pair assocPair : assocPairs)
+ {
+ AssociationRef assocRef = assocPair.getSecond();
+ // check qname pattern, if not already filtered
+ if (qnameFilter == null && !qnamePattern.isMatch(assocRef.getTypeQName()))
+ {
+ continue; // the assoc name doesn't match the pattern given
+ }
+ nodeAssocRefs.add(assocRef);
+ }
+ // done
+ return nodeAssocRefs;
+ }
+
public List getSourceAssocs(NodeRef targetRef, QNamePattern qnamePattern)
{
Pair targetNodePair = getNodePairNotNull(targetRef);
diff --git a/source/java/org/alfresco/repo/version/Node2ServiceImpl.java b/source/java/org/alfresco/repo/version/Node2ServiceImpl.java
index df219816f4..f12b6277b5 100644
--- a/source/java/org/alfresco/repo/version/Node2ServiceImpl.java
+++ b/source/java/org/alfresco/repo/version/Node2ServiceImpl.java
@@ -238,4 +238,57 @@ public class Node2ServiceImpl extends NodeServiceImpl implements NodeService, Ve
return result;
}
+
+ /**
+ * {@inheritDoc}
+ *
+ *
+ * Implementation for version store v2
+ */
+ @Override
+ public List getTargetAssocsByPropertyValue(NodeRef sourceRef, QNamePattern qnamePattern, QName propertyQName, Serializable propertyValue)
+ {
+ // If lightWeightVersionStore call default version store implementation.
+ if (sourceRef.getStoreRef().getIdentifier().equals(VersionModel.STORE_ID))
+ {
+ return super.getTargetAssocsByPropertyValue(sourceRef, qnamePattern, propertyQName, propertyValue);
+ }
+
+ // Get the assoc references from the version store.
+ List childAssocRefs = this.dbNodeService.getChildAssocs(VersionUtil.convertNodeRef(sourceRef),
+ Version2Model.CHILD_QNAME_VERSIONED_ASSOCS, qnamePattern);
+
+ List result = new ArrayList(childAssocRefs.size());
+
+ for (ChildAssociationRef childAssocRef : childAssocRefs)
+ {
+ // Get the assoc reference.
+ NodeRef childRef = childAssocRef.getChildRef();
+ NodeRef referencedNode = (NodeRef) this.dbNodeService.getProperty(childRef, ContentModel.PROP_REFERENCE);
+
+ if (this.dbNodeService.exists(referencedNode))
+ {
+ Long assocDbId = (Long) this.dbNodeService.getProperty(childRef, Version2Model.PROP_QNAME_ASSOC_DBID);
+
+ // Check if property type validation has to be done.
+ if (propertyQName != null)
+ {
+ Serializable propertyValueRetrieved = this.dbNodeService.getProperty(referencedNode, propertyQName);
+
+ // Check if property value has been retrieved (property
+ // exists) and is equal to the requested value.
+ if (propertyValueRetrieved == null || !propertyValueRetrieved.equals(propertyValue))
+ {
+ continue;
+ }
+ }
+
+ // Build an assoc ref to add to the returned list.
+ AssociationRef newAssocRef = new AssociationRef(assocDbId, sourceRef, childAssocRef.getQName(), referencedNode);
+ result.add(newAssocRef);
+ }
+ }
+
+ return result;
+ }
}
diff --git a/source/java/org/alfresco/repo/version/NodeServiceImpl.java b/source/java/org/alfresco/repo/version/NodeServiceImpl.java
index 845a125d3a..eec631a1d9 100644
--- a/source/java/org/alfresco/repo/version/NodeServiceImpl.java
+++ b/source/java/org/alfresco/repo/version/NodeServiceImpl.java
@@ -69,6 +69,8 @@ public class NodeServiceImpl implements NodeService, VersionModel
*/
protected final static String MSG_UNSUPPORTED =
"This operation is not supported by a version store implementation of the node service.";
+
+ private final static String MSG_UNSUPPORTED_V1 = "Versioning V1 is not implemented or supported. Patches exist to upgrade your data to use Versioning V2. Please contact support.";
/**
* The name of the spoofed root association
@@ -691,6 +693,16 @@ public class NodeServiceImpl implements NodeService, VersionModel
return result;
}
+ /**
+ * @throws UnsupportedOperationException always
+ */
+ @Override
+ public List getTargetAssocsByPropertyValue(NodeRef sourceRef, QNamePattern qnamePattern, QName propertyQName, Serializable propertyValue)
+ {
+ // This operation is not supported for versioning V1
+ throw new UnsupportedOperationException(MSG_UNSUPPORTED_V1);
+ }
+
/**
* @throws UnsupportedOperationException always
*/
diff --git a/source/test-java/org/alfresco/repo/node/BaseNodeServiceTest.java b/source/test-java/org/alfresco/repo/node/BaseNodeServiceTest.java
index 32e3ae2095..f0b11933cf 100644
--- a/source/test-java/org/alfresco/repo/node/BaseNodeServiceTest.java
+++ b/source/test-java/org/alfresco/repo/node/BaseNodeServiceTest.java
@@ -75,6 +75,7 @@ import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.BaseSpringTest;
import org.alfresco.util.GUID;
+import org.alfresco.util.Pair;
import org.hibernate.dialect.DB2Dialect;
import org.hibernate.dialect.Dialect;
import org.springframework.context.ApplicationContext;
@@ -2805,7 +2806,79 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
setComplete();
endTransaction();
}
-
+
+ /**
+ * Tests get target associations by property value.
+ * See MNT-14504 for more details.
+ *
+ * @throws Exception
+ */
+ public void testGetTargetAssocsByPropertyValue() throws Exception
+ {
+ // Create test data.
+ AssociationRef assocRef = createAssociation();
+ NodeRef sourceRef = assocRef.getSourceRef();
+ createAssociation(sourceRef);
+
+ NodeRef targetRef = assocRef.getTargetRef();
+ QName qname = assocRef.getTypeQName();
+
+ /* Positive tests of various types that should be accepted by the query. */
+
+ List targetAssocs = nodeService.getTargetAssocsByPropertyValue(sourceRef, qname, null, null);
+ assertEquals("Incorrect number of targets", 2, targetAssocs.size());
+
+ Map checkProperties = new HashMap();
+ checkProperties.put(ContentModel.PROP_ENABLED, Boolean.TRUE);
+ checkProperties.put(ContentModel.PROP_COUNTER, 100);
+ checkProperties.put(ContentModel.PROP_LATITUDE, new Double(51.521));
+ checkProperties.put(ContentModel.PROP_SUBJECT, "Hello World");
+
+ for (QName propertyQName : checkProperties.keySet())
+ {
+ Serializable propertyValue = checkProperties.get(propertyQName);
+ nodeService.setProperty(targetRef, propertyQName, propertyValue);
+
+ targetAssocs = nodeService.getTargetAssocsByPropertyValue(sourceRef, qname, propertyQName, propertyValue);
+ assertEquals("Incorrect number of targets", 1, targetAssocs.size());
+ assertTrue("Target not found", targetAssocs.contains(assocRef));
+
+ AssociationRef targetAssoc = targetAssocs.get(0);
+
+ // Check that ID is present
+ assertNotNull("Association does not have ID", targetAssoc.getId());
+
+ NodeRef targetRefFound = targetAssoc.getTargetRef();
+
+ assertEquals("Incorrect value found", propertyValue, this.nodeService.getProperty(targetRefFound, propertyQName));
+ }
+
+ /* Negative tests. */
+
+ // Invalid to search on sys:node-dbid
+ try
+ {
+ targetAssocs = nodeService.getTargetAssocsByPropertyValue(sourceRef, qname, ContentModel.PROP_NODE_DBID, "Fail");
+ fail("sys:node-dbid not rejected");
+ }
+ catch (IllegalArgumentException ie)
+ {
+ // Expect to go here.
+ }
+
+ // Invalid to search on type MLText.
+ try
+ {
+ Serializable title = (String) nodeService.getProperty(sourceRef, ContentModel.PROP_TITLE);
+ targetAssocs = nodeService.getTargetAssocsByPropertyValue(sourceRef, qname, ContentModel.PROP_NAME, title);
+ fail("MLText type not rejected");
+ }
+ catch (IllegalArgumentException ie)
+ {
+ // Expect to go here.
+ }
+ }
+
public void testGetSourceAssocs() throws Exception
{
AssociationRef assocRef = createAssociation();
diff --git a/source/test-java/org/alfresco/repo/version/NodeServiceImplTest.java b/source/test-java/org/alfresco/repo/version/NodeServiceImplTest.java
index b23865c3b1..82df59f030 100644
--- a/source/test-java/org/alfresco/repo/version/NodeServiceImplTest.java
+++ b/source/test-java/org/alfresco/repo/version/NodeServiceImplTest.java
@@ -263,7 +263,31 @@ public class NodeServiceImplTest extends BaseVersionStoreTest
assertNotNull(assocs);
assertEquals(origAssocs.size(), assocs.size());
}
-
+
+ /**
+ * Tests get target associations by property value.
+ * See MNT-14504 for more details.
+ */
+ public void testGetTargetAssocsByPropertyValue()
+ {
+ // Create a new versionable node
+ NodeRef versionableNode = createNewVersionableNode();
+
+ QName propertyQName = PROP_1;
+ Serializable propertyValue = VALUE_1;
+ // Store the current details of the target associations
+ List origAssocs = this.dbNodeService.getTargetAssocsByPropertyValue(versionableNode, RegexQNamePattern.MATCH_ALL,
+ propertyQName, propertyValue);
+
+ // Create a new version
+ Version version = createVersion(versionableNode, this.versionProperties);
+
+ List assocs = this.versionStoreNodeService.getTargetAssocsByPropertyValue(version.getFrozenStateNodeRef(),
+ RegexQNamePattern.MATCH_ALL, propertyQName, propertyValue);
+ assertNotNull(assocs);
+ assertEquals(origAssocs.size(), assocs.size());
+ }
+
/**
* Test hasAspect
*/