Fix SAIL-389 (SAIL-294): NodeDAO: single-valued, d:any properties don't handle increasing array values

- Incorrect translation of raw values back to Serializable for cache purposes
 - Addition of Savepoint around alf_node insert


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@21136 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley
2010-07-13 15:22:53 +00:00
parent 297309f1aa
commit e7dff0383a
4 changed files with 74 additions and 16 deletions

View File

@@ -938,13 +938,16 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
} }
Long id = null; Long id = null;
Savepoint savepoint = controlDAO.createSavepoint("newNodeImpl");
try try
{ {
// First try a straight insert and risk the constraint violation if the node exists // First try a straight insert and risk the constraint violation if the node exists
id = insertNode(node); id = insertNode(node);
controlDAO.releaseSavepoint(savepoint);
} }
catch (Throwable e) catch (Throwable e)
{ {
controlDAO.rollbackToSavepoint(savepoint);
// This is probably because there is an existing node. We can handle existing deleted nodes. // This is probably because there is an existing node. We can handle existing deleted nodes.
NodeRef targetNodeRef = node.getNodeRef(); NodeRef targetNodeRef = node.getNodeRef();
NodeEntity deletedNode = selectNodeByNodeRef(targetNodeRef, true); // Only look for deleted nodes NodeEntity deletedNode = selectNodeByNodeRef(targetNodeRef, true); // Only look for deleted nodes
@@ -3071,7 +3074,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
protected abstract Map<NodePropertyKey, NodePropertyValue> selectNodeProperties(Long nodeId); protected abstract Map<NodePropertyKey, NodePropertyValue> selectNodeProperties(Long nodeId);
protected abstract Map<NodePropertyKey, NodePropertyValue> selectNodeProperties(Long nodeId, Set<Long> qnameIds); protected abstract Map<NodePropertyKey, NodePropertyValue> selectNodeProperties(Long nodeId, Set<Long> qnameIds);
protected abstract int deleteNodeProperties(Long nodeId, Set<Long> qnameIds); protected abstract int deleteNodeProperties(Long nodeId, Set<Long> qnameIds);
protected abstract void deleteNodeProperties(Long nodeId, List<NodePropertyKey> propKeys); protected abstract int deleteNodeProperties(Long nodeId, List<NodePropertyKey> propKeys);
protected abstract void insertNodeProperties(Long nodeId, Map<NodePropertyKey, NodePropertyValue> persistableProps); protected abstract void insertNodeProperties(Long nodeId, Map<NodePropertyKey, NodePropertyValue> persistableProps);
protected abstract Set<Long> selectNodeAspectIds(Long nodeId); protected abstract Set<Long> selectNodeAspectIds(Long nodeId);
protected abstract void insertNodeAspect(Long nodeId, Long qnameId); protected abstract void insertNodeAspect(Long nodeId, Long qnameId);

View File

@@ -191,9 +191,10 @@ public class NodePropertyHelper
// Check that multi-valued properties are supported if the property is a collection // Check that multi-valued properties are supported if the property is a collection
if (!isMultiValued) if (!isMultiValued)
{ {
throw new DictionaryException("A single-valued property of this type may not be a collection: \n" throw new DictionaryException("A single-valued property of this type may not be a collection: \n" +
+ " Property: " + propertyDef + "\n" + " Type: " + propertyTypeQName + "\n" + " Value: " " Property: " + propertyDef + "\n" +
+ value); " Type: " + propertyTypeQName + "\n" +
" Value: " + value);
} }
// We have an allowable collection. // We have an allowable collection.
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@@ -327,8 +328,7 @@ public class NodePropertyHelper
} }
public Serializable getPublicProperty( public Serializable getPublicProperty(
Map<NodePropertyKey, Map<NodePropertyKey, NodePropertyValue> propertyValues,
NodePropertyValue> propertyValues,
QName propertyQName) QName propertyQName)
{ {
// Get the qname ID // Get the qname ID
@@ -409,16 +409,24 @@ public class NodePropertyHelper
// There is more than one value so the list indexes need to be collapsed // There is more than one value so the list indexes need to be collapsed
collapsedValue = collapsePropertiesWithSameQName(currentPropertyDef, scratch); collapsedValue = collapsePropertiesWithSameQName(currentPropertyDef, scratch);
} }
boolean forceCollection = false;
// If the property is multi-valued then the output property must be a collection // If the property is multi-valued then the output property must be a collection
if (currentPropertyDef != null && currentPropertyDef.isMultiValued()) if (currentPropertyDef != null && currentPropertyDef.isMultiValued())
{ {
if (collapsedValue != null && !(collapsedValue instanceof Collection<?>)) forceCollection = true;
{ }
// Can't use Collections.singletonList: ETHREEOH-1172 else if (scratch.size() == 1 && scratch.firstKey().getListIndex().intValue() > -1)
ArrayList<Serializable> collection = new ArrayList<Serializable>(1); {
collection.add(collapsedValue); // This is to handle cases of collections where the property is d:any but not
collapsedValue = collection; // declared as multiple.
} forceCollection = true;
}
if (forceCollection && collapsedValue != null && !(collapsedValue instanceof Collection<?>))
{
// Can't use Collections.singletonList: ETHREEOH-1172
ArrayList<Serializable> collection = new ArrayList<Serializable>(1);
collection.add(collapsedValue);
collapsedValue = collection;
} }
// Store the value // Store the value
propertyMap.put(currentQName, collapsedValue); propertyMap.put(currentQName, collapsedValue);
@@ -534,12 +542,23 @@ public class NodePropertyHelper
if (propertyValuesSize == 0) if (propertyValuesSize == 0)
{ {
// Nothing to do // Nothing to do
return value;
} }
Integer listIndex = null;
for (Map.Entry<NodePropertyKey, NodePropertyValue> entry : propertyValues.entrySet()) for (Map.Entry<NodePropertyKey, NodePropertyValue> entry : propertyValues.entrySet())
{ {
NodePropertyKey propertyKey = entry.getKey(); NodePropertyKey propertyKey = entry.getKey();
NodePropertyValue propertyValue = entry.getValue(); NodePropertyValue propertyValue = entry.getValue();
if (listIndex == null)
{
listIndex = propertyKey.getListIndex();
}
else if (!listIndex.equals(propertyKey.getListIndex()))
{
throw new IllegalStateException("Expecting to collapse properties with same list index: " + propertyValues);
}
if (propertyValuesSize == 1 if (propertyValuesSize == 1
&& (propertyDef == null || !propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT))) && (propertyDef == null || !propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT)))
{ {

View File

@@ -468,14 +468,14 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
} }
@Override @Override
protected void deleteNodeProperties(Long nodeId, List<NodePropertyKey> propKeys) protected int deleteNodeProperties(Long nodeId, List<NodePropertyKey> propKeys)
{ {
Assert.notNull(nodeId, "Must have 'nodeId'"); Assert.notNull(nodeId, "Must have 'nodeId'");
Assert.notNull(nodeId, "Must have 'propKeys'"); Assert.notNull(nodeId, "Must have 'propKeys'");
if (propKeys.size() == 0) if (propKeys.size() == 0)
{ {
return; return 0;
} }
NodePropertyEntity prop = new NodePropertyEntity(); NodePropertyEntity prop = new NodePropertyEntity();
@@ -483,18 +483,20 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
prop.setNodeId(nodeId); prop.setNodeId(nodeId);
startBatch(); startBatch();
int count = 0;
try try
{ {
for (NodePropertyKey propKey : propKeys) for (NodePropertyKey propKey : propKeys)
{ {
prop.setKey(propKey); prop.setKey(propKey);
template.delete(DELETE_NODE_PROPERTIES, prop); count += template.delete(DELETE_NODE_PROPERTIES, prop);
} }
} }
finally finally
{ {
executeBatch(); executeBatch();
} }
return count;
} }
@Override @Override

View File

@@ -1892,6 +1892,40 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
assertEquals("MLText collection didn't come back correctly.", mlTextCollection, mlTextCollectionCheck); assertEquals("MLText collection didn't come back correctly.", mlTextCollection, mlTextCollectionCheck);
} }
/**
* Ensures that d:any types are handled correctly when adding values
*/
public void testMultivaluedSerializable() throws Exception
{
ArrayList<String> listProp = new ArrayList<String>();
listProp.clear();
nodeService.addProperties(
rootNodeRef,
Collections.singletonMap(PROP_QNAME_ANY_PROP_MULTIPLE, (Serializable) listProp));
listProp.add("ONE");
nodeService.addProperties(
rootNodeRef,
Collections.singletonMap(PROP_QNAME_ANY_PROP_MULTIPLE, (Serializable) listProp));
listProp.add("TWO");
nodeService.addProperties(
rootNodeRef,
Collections.singletonMap(PROP_QNAME_ANY_PROP_MULTIPLE, (Serializable) listProp));
listProp.clear();
nodeService.addProperties(
rootNodeRef,
Collections.singletonMap(PROP_QNAME_ANY_PROP_SINGLE, (Serializable) listProp));
listProp.add("ONE");
nodeService.addProperties(
rootNodeRef,
Collections.singletonMap(PROP_QNAME_ANY_PROP_SINGLE, (Serializable) listProp));
listProp.add("TWO");
nodeService.addProperties(
rootNodeRef,
Collections.singletonMap(PROP_QNAME_ANY_PROP_SINGLE, (Serializable) listProp));
}
/** /**
* Checks that the {@link ContentModel#ASPECT_REFERENCEABLE referencable} properties * Checks that the {@link ContentModel#ASPECT_REFERENCEABLE referencable} properties
* are present * are present