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 */