Node cache changes (step): properties and aspects are retrieved with the node's version

- Perform basic node version check when reading properties and aspects from DB
 - This is just the start.  Next step is to change the key of the cache itself.
 - Includes fix for rev 31109: Use setNodeAclId() to set ACL IDs


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@31121 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley
2011-10-11 10:58:48 +00:00
parent d961c8f966
commit 87bee8092d
8 changed files with 262 additions and 99 deletions

View File

@@ -82,6 +82,7 @@
<resultMap id="result_NodeAspects" type="NodeAspects" groupBy="nodeId"> --> <resultMap id="result_NodeAspects" type="NodeAspects" groupBy="nodeId"> -->
<resultMap id="result_NodeAspects" type="NodeAspects"> <resultMap id="result_NodeAspects" type="NodeAspects">
<id property="nodeId" column="node_id" jdbcType="BIGINT" javaType="java.lang.Long"/> <id property="nodeId" column="node_id" jdbcType="BIGINT" javaType="java.lang.Long"/>
<id property="nodeVersion" column="node_version" jdbcType="BIGINT" javaType="java.lang.Long"/>
<association property="aspectQNameIds" resultMap="alfresco.node.result_AspectQNameIds"/> <association property="aspectQNameIds" resultMap="alfresco.node.result_AspectQNameIds"/>
<!-- <!--
<result property="aspectQNameIds" column="qname_id" jdbcType="BIGINT" javaType="java.lang.Long"/> <result property="aspectQNameIds" column="qname_id" jdbcType="BIGINT" javaType="java.lang.Long"/>
@@ -89,6 +90,7 @@
</resultMap> </resultMap>
<resultMap id="result_NodeProperty" type="NodeProperty"> <resultMap id="result_NodeProperty" type="NodeProperty">
<result property="nodeId" column="node_id" jdbcType="BIGINT" javaType="java.lang.Long"/> <result property="nodeId" column="node_id" jdbcType="BIGINT" javaType="java.lang.Long"/>
<result property="nodeVersion" column="node_version" jdbcType="BIGINT" javaType="java.lang.Long"/>
<result property="key.qnameId" column="qname_id" jdbcType="BIGINT" javaType="java.lang.Long"/> <result property="key.qnameId" column="qname_id" jdbcType="BIGINT" javaType="java.lang.Long"/>
<result property="key.localeId" column="locale_id" jdbcType="BIGINT" javaType="java.lang.Long"/> <result property="key.localeId" column="locale_id" jdbcType="BIGINT" javaType="java.lang.Long"/>
<result property="key.listIndex" column="list_index" jdbcType="INTEGER" javaType="java.lang.Integer"/> <result property="key.listIndex" column="list_index" jdbcType="INTEGER" javaType="java.lang.Integer"/>
@@ -720,7 +722,8 @@
<sql id="select_NodeProperty_Results"> <sql id="select_NodeProperty_Results">
select select
prop.node_id as node_id, node.id as node_id,
node.version as node_version,
prop.qname_id as qname_id, prop.qname_id as qname_id,
prop.locale_id as locale_id, prop.locale_id as locale_id,
prop.list_index as list_index, prop.list_index as list_index,
@@ -737,7 +740,8 @@
<select id="select_NodeProperties" parameterType="NodeProperty" resultMap="result_NodeProperty"> <select id="select_NodeProperties" parameterType="NodeProperty" resultMap="result_NodeProperty">
<include refid="alfresco.node.select_NodeProperty_Results"/> <include refid="alfresco.node.select_NodeProperty_Results"/>
from from
alf_node_properties prop alf_node node
join alf_node_properties prop on (prop.node_id = node.id)
<where> <where>
<if test="nodeId != null">prop.node_id = #{nodeId}</if> <if test="nodeId != null">prop.node_id = #{nodeId}</if>
<if test="nodeIds != null"> <if test="nodeIds != null">
@@ -759,7 +763,8 @@
<select id="select_PropertiesByTypes" parameterType="Ids" resultMap="result_NodeProperty"> <select id="select_PropertiesByTypes" parameterType="Ids" resultMap="result_NodeProperty">
<include refid="alfresco.node.select_NodeProperty_Results"/> <include refid="alfresco.node.select_NodeProperty_Results"/>
from from
alf_node_properties prop alf_node node
join alf_node_properties prop on (prop.node_id = node.id)
<where> <where>
qname_id in qname_id in
<foreach item="item" index="index" collection="ids" open="(" separator="," close=")"> <foreach item="item" index="index" collection="ids" open="(" separator="," close=")">
@@ -770,14 +775,16 @@
<sql id="select_NodeAspects_Results"> <sql id="select_NodeAspects_Results">
select select
aspects.node_id as node_id, node.id as node_id,
node.version as node_version,
aspects.qname_id as qname_id aspects.qname_id as qname_id
</sql> </sql>
<select id="select_NodeAspects" parameterType="Ids" resultMap="result_NodeAspects"> <select id="select_NodeAspects" parameterType="Ids" resultMap="result_NodeAspects">
<include refid="alfresco.node.select_NodeAspects_Results"/> <include refid="alfresco.node.select_NodeAspects_Results"/>
from from
alf_node_aspects aspects alf_node node
join alf_node_aspects aspects on (aspects.node_id = node.id)
<where> <where>
<if test="nodeId != null">aspects.node_id = #{nodeId}</if> <if test="nodeId != null">aspects.node_id = #{nodeId}</if>
<if test="nodeIds != null"> <if test="nodeIds != null">
@@ -786,15 +793,6 @@
#{item} #{item}
</foreach> </foreach>
</if> </if>
<!--
<if test="key != null and key.qnameId">and qname_id = #{key.qnameId}</if>
<if test="qnameIds">
and qname_id in
<foreach item="item" index="index" collection="qnameIds" open="(" separator="," close=")">
#{item}
</foreach>
</if>
-->
</where> </where>
</select> </select>

View File

@@ -562,6 +562,9 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
} }
} }
/**
* @return Returns a new transaction or an existing one if already active
*/
private TransactionEntity getCurrentTransaction() private TransactionEntity getCurrentTransaction()
{ {
TransactionEntity txn = AlfrescoTransactionSupport.getResource(KEY_TRANSACTION); TransactionEntity txn = AlfrescoTransactionSupport.getResource(KEY_TRANSACTION);
@@ -1216,6 +1219,9 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
} }
} }
// Need the child node's name here in case it gets removed
final String childNodeName = (String) getNodeProperty(childNodeId, ContentModel.PROP_NAME);
// First attempt to move the node, which may rollback to a savepoint // First attempt to move the node, which may rollback to a savepoint
Node newChildNode = childNode; Node newChildNode = childNode;
// Store // Store
@@ -1281,11 +1287,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
// Because we are retrying in-transaction i.e. absorbing exceptions, we need a Savepoint // Because we are retrying in-transaction i.e. absorbing exceptions, we need a Savepoint
Savepoint savepoint = controlDAO.createSavepoint("DuplicateChildNodeNameException"); Savepoint savepoint = controlDAO.createSavepoint("DuplicateChildNodeNameException");
// We use the child node's UUID if there is no cm:name // We use the child node's UUID if there is no cm:name
String childNodeName = (String) getNodeProperty(childNodeId, ContentModel.PROP_NAME); String childNodeNameToUse = childNodeName == null ? childNode.getUuid() : childNodeName;
if (childNodeName == null)
{
childNodeName = childNode.getUuid();
}
try try
{ {
@@ -1294,7 +1296,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
newParentNodeId, newParentNodeId,
assocTypeQName, assocTypeQName,
assocQName, assocQName,
childNodeName); childNodeNameToUse);
controlDAO.releaseSavepoint(savepoint); controlDAO.releaseSavepoint(savepoint);
return updated; return updated;
} }
@@ -1565,7 +1567,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
Long optionalOldSharedAlcIdInAdditionToNull, Long optionalOldSharedAlcIdInAdditionToNull,
Long newSharedAclId) Long newSharedAclId)
{ {
Long txnId = getCurrentTransactionId(); Long txnId = getCurrentTransaction().getId();
updatePrimaryChildrenSharedAclId( updatePrimaryChildrenSharedAclId(
txnId, txnId,
primaryParentNodeId, primaryParentNodeId,
@@ -2101,6 +2103,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
throw new DataIntegrityViolationException("Invalid node ID: " + nodeId); throw new DataIntegrityViolationException("Invalid node ID: " + nodeId);
} }
Map<QName, Serializable> cachedProperties = cacheEntry.getSecond(); Map<QName, Serializable> cachedProperties = cacheEntry.getSecond();
// Need to return a harmlessly mutable map
Map<QName, Serializable> properties = copyPropertiesAgainstModification(cachedProperties); Map<QName, Serializable> properties = copyPropertiesAgainstModification(cachedProperties);
// Done // Done
return properties; return properties;
@@ -2153,7 +2156,27 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
public Pair<Long, Map<QName, Serializable>> findByKey(Long nodeId) public Pair<Long, Map<QName, Serializable>> findByKey(Long nodeId)
{ {
Map<NodePropertyKey, NodePropertyValue> propsRaw = selectNodeProperties(nodeId); NodeVersionKey nodeVersionKey = getNodeNotNull(nodeId).getNodeVersionKey();
Map<NodeVersionKey, Map<NodePropertyKey, NodePropertyValue>> propsRawByNodeVersionKey = selectNodeProperties(nodeId);
// Check the node Txn ID for mismatch
Map<NodePropertyKey, NodePropertyValue> propsRaw = propsRawByNodeVersionKey.get(nodeVersionKey);
if (propsRaw == null)
{
// Didn't find a match. Is this because there are none?
if (propsRawByNodeVersionKey.size() == 0)
{
// This is OK. The node has no properties
propsRaw = Collections.emptyMap();
}
else
{
// We found properties associated with a different node ID and txn
invalidateNodeCaches(nodeId);
throw new DataIntegrityViolationException(
"Detected stale node entry: " + nodeVersionKey +
" (now " + propsRawByNodeVersionKey.keySet() + ")");
}
}
// Convert to public properties // Convert to public properties
Map<QName, Serializable> props = nodePropertyHelper.convertToPublicProperties(propsRaw); Map<QName, Serializable> props = nodePropertyHelper.convertToPublicProperties(propsRaw);
// Done // Done
@@ -2351,9 +2374,27 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
public Pair<Long, Set<QName>> findByKey(Long nodeId) public Pair<Long, Set<QName>> findByKey(Long nodeId)
{ {
Set<Long> nodeAspectQNameIds = selectNodeAspectIds(nodeId); NodeVersionKey nodeVersionKey = getNodeNotNull(nodeId).getNodeVersionKey();
// Convert to QNames Set<Long> nodeIds = Collections.singleton(nodeId);
Set<QName> nodeAspectQNames = qnameDAO.convertIdsToQNames(nodeAspectQNameIds); Map<NodeVersionKey, Set<QName>> nodeAspectQNameIdsByVersionKey = selectNodeAspects(nodeIds);
Set<QName> nodeAspectQNames = nodeAspectQNameIdsByVersionKey.get(nodeVersionKey);
if (nodeAspectQNames == null)
{
// Didn't find a match. Is this because there are none?
if (nodeAspectQNameIdsByVersionKey.size() == 0)
{
// This is OK. The node has no properties
nodeAspectQNames = Collections.emptySet();
}
else
{
// We found properties associated with a different node ID and txn
invalidateNodeCaches(nodeId);
throw new DataIntegrityViolationException(
"Detected stale node entry: " + nodeVersionKey +
" (now " + nodeAspectQNameIdsByVersionKey.keySet() + ")");
}
}
// Done // Done
return new Pair<Long, Set<QName>>(nodeId, Collections.unmodifiableSet(nodeAspectQNames)); return new Pair<Long, Set<QName>>(nodeId, Collections.unmodifiableSet(nodeAspectQNames));
} }
@@ -3619,9 +3660,11 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
// Get the nodes // Get the nodes
SortedSet<Long> aspectNodeIds = new TreeSet<Long>(); SortedSet<Long> aspectNodeIds = new TreeSet<Long>();
SortedSet<Long> propertiesNodeIds = new TreeSet<Long>(); SortedSet<Long> propertiesNodeIds = new TreeSet<Long>();
Map<Long, NodeVersionKey> nodeVersionKeysFromCache = new HashMap<Long, NodeVersionKey>(nodes.size()*2); // Keep for quick lookup
for (Node node : nodes) for (Node node : nodes)
{ {
Long nodeId = node.getId(); Long nodeId = node.getId();
NodeVersionKey nodeVersionKey = node.getNodeVersionKey();
nodesCache.setValue(nodeId, node); nodesCache.setValue(nodeId, node);
if (propertiesCache.getValue(nodeId) == null) if (propertiesCache.getValue(nodeId) == null)
{ {
@@ -3631,6 +3674,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
{ {
aspectNodeIds.add(nodeId); aspectNodeIds.add(nodeId);
} }
nodeVersionKeysFromCache.put(nodeId, nodeVersionKey);
} }
if(logger.isDebugEnabled()) if(logger.isDebugEnabled())
@@ -3639,14 +3683,13 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
logger.debug("Pre-loaded " + propertiesNodeIds.size() + " aspects"); logger.debug("Pre-loaded " + propertiesNodeIds.size() + " aspects");
} }
List<NodeAspectsEntity> nodeAspects = selectNodeAspects(aspectNodeIds); Map<NodeVersionKey, Set<QName>> nodeAspects = selectNodeAspects(aspectNodeIds);
for (NodeAspectsEntity nodeAspect : nodeAspects) for (Map.Entry<NodeVersionKey, Set<QName>> entry : nodeAspects.entrySet())
{ {
Long nodeId = nodeAspect.getNodeId(); NodeVersionKey nodeVersionKeyFromDb = entry.getKey();
List<Long> qnameIds = nodeAspect.getAspectQNameIds(); Long nodeId = nodeVersionKeyFromDb.getNodeId();
HashSet<Long> qnameIdsSet = new HashSet<Long>(qnameIds); Set<QName> qnames = entry.getValue();
Set<QName> qnames = qnameDAO.convertIdsToQNames(qnameIdsSet); setNodeAspectsCached(nodeId, qnames);
aspectsCache.setValue(nodeId, qnames);
aspectNodeIds.remove(nodeId); aspectNodeIds.remove(nodeId);
} }
// Cache the absence of aspects too! // Cache the absence of aspects too!
@@ -3655,13 +3698,13 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
aspectsCache.setValue(nodeId, Collections.<QName>emptySet()); aspectsCache.setValue(nodeId, Collections.<QName>emptySet());
} }
Map<Long, Map<NodePropertyKey, NodePropertyValue>> propsByNodeId = selectNodeProperties(propertiesNodeIds); Map<NodeVersionKey, Map<NodePropertyKey, NodePropertyValue>> propsByNodeId = selectNodeProperties(propertiesNodeIds);
for (Map.Entry<Long, Map<NodePropertyKey, NodePropertyValue>> entry : propsByNodeId.entrySet()) for (Map.Entry<NodeVersionKey, Map<NodePropertyKey, NodePropertyValue>> entry : propsByNodeId.entrySet())
{ {
Long nodeId = entry.getKey(); Long nodeId = entry.getKey().getNodeId();
Map<NodePropertyKey, NodePropertyValue> propertyValues = entry.getValue(); Map<NodePropertyKey, NodePropertyValue> propertyValues = entry.getValue();
Map<QName, Serializable> props = nodePropertyHelper.convertToPublicProperties(propertyValues); Map<QName, Serializable> props = nodePropertyHelper.convertToPublicProperties(propertyValues);
propertiesCache.setValue(nodeId, props); setNodePropertiesCached(nodeId, props);
} }
} }
@@ -3847,14 +3890,13 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
protected abstract NodeEntity selectNodeByNodeRef(NodeRef nodeRef, Boolean deleted); protected abstract NodeEntity selectNodeByNodeRef(NodeRef nodeRef, Boolean deleted);
protected abstract List<Node> selectNodesByUuids(Long storeId, SortedSet<String> uuids); protected abstract List<Node> selectNodesByUuids(Long storeId, SortedSet<String> uuids);
protected abstract List<Node> selectNodesByIds(SortedSet<Long> ids); protected abstract List<Node> selectNodesByIds(SortedSet<Long> ids);
protected abstract Map<Long, Map<NodePropertyKey, NodePropertyValue>> selectNodeProperties(Set<Long> nodeIds); protected abstract Map<NodeVersionKey, Map<NodePropertyKey, NodePropertyValue>> selectNodeProperties(Set<Long> nodeIds);
protected abstract List<NodeAspectsEntity> selectNodeAspects(Set<Long> nodeIds); protected abstract Map<NodeVersionKey, Map<NodePropertyKey, NodePropertyValue>> selectNodeProperties(Long nodeId);
protected abstract Map<NodePropertyKey, NodePropertyValue> selectNodeProperties(Long nodeId); protected abstract Map<NodeVersionKey, 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 int 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 Map<NodeVersionKey, Set<QName>> selectNodeAspects(Set<Long> nodeIds);
protected abstract void insertNodeAspect(Long nodeId, Long qnameId); protected abstract void insertNodeAspect(Long nodeId, Long qnameId);
protected abstract int deleteNodeAspects(Long nodeId, Set<Long> qnameIds); protected abstract int deleteNodeAspects(Long nodeId, Set<Long> qnameIds);
protected abstract void selectNodesWithAspects( protected abstract void selectNodesWithAspects(

View File

@@ -28,6 +28,11 @@ import org.alfresco.util.Pair;
*/ */
public interface Node extends NodeIdAndAclId public interface Node extends NodeIdAndAclId
{ {
/**
* Helper method to get a key that includes the node and its current version number
*/
public NodeVersionKey getNodeVersionKey();
/** /**
* Helper method to force the instance to be read-only * Helper method to force the instance to be read-only
*/ */

View File

@@ -29,6 +29,7 @@ import java.util.List;
public class NodeAspectsEntity public class NodeAspectsEntity
{ {
private Long nodeId; private Long nodeId;
private Long nodeVersion;
private List<Long> aspectQNameIds; private List<Long> aspectQNameIds;
/** Carries data for queries */ /** Carries data for queries */
@@ -47,6 +48,7 @@ public class NodeAspectsEntity
StringBuilder sb = new StringBuilder(512); StringBuilder sb = new StringBuilder(512);
sb.append("NodeAspectsEntity") sb.append("NodeAspectsEntity")
.append(", nodeId=").append(nodeId) .append(", nodeId=").append(nodeId)
.append(", nodeVersion=").append(nodeVersion)
.append(", aspects=").append(aspectQNameIds) .append(", aspects=").append(aspectQNameIds)
.append("]"); .append("]");
return sb.toString(); return sb.toString();
@@ -62,6 +64,16 @@ public class NodeAspectsEntity
this.nodeId = nodeId; this.nodeId = nodeId;
} }
public Long getNodeVersion()
{
return nodeVersion;
}
public void setNodeVersion(Long nodeVersion)
{
this.nodeVersion = nodeVersion;
}
public List<Long> getAspectQNameIds() public List<Long> getAspectQNameIds()
{ {
return aspectQNameIds; return aspectQNameIds;

View File

@@ -105,6 +105,17 @@ public class NodeEntity implements Node, PermissionCheckValue
return sb.toString(); return sb.toString();
} }
@Override
// TODO: Must cache the key
public NodeVersionKey getNodeVersionKey()
{
if (id == null || version == null)
{
throw new IllegalStateException("The NodeEntity has not be filled: " + this);
}
return new NodeVersionKey(id, version);
}
/** /**
* Lock the entity against further updates to prevent accidental modification * Lock the entity against further updates to prevent accidental modification
*/ */
@@ -125,8 +136,9 @@ public class NodeEntity implements Node, PermissionCheckValue
} }
} }
public void incrementVersion() public synchronized void incrementVersion()
{ {
checkLock();
if (version >= Short.MAX_VALUE) if (version >= Short.MAX_VALUE)
{ {
this.version = 0L; this.version = 0L;
@@ -162,7 +174,7 @@ public class NodeEntity implements Node, PermissionCheckValue
return id; return id;
} }
public void setId(Long id) public synchronized void setId(Long id)
{ {
checkLock(); checkLock();
this.id = id; this.id = id;
@@ -174,7 +186,7 @@ public class NodeEntity implements Node, PermissionCheckValue
return version; return version;
} }
public void setVersion(Long version) public synchronized void setVersion(Long version)
{ {
checkLock(); checkLock();
this.version = version; this.version = version;
@@ -186,7 +198,7 @@ public class NodeEntity implements Node, PermissionCheckValue
return store; return store;
} }
public void setStore(StoreEntity store) public synchronized void setStore(StoreEntity store)
{ {
checkLock(); checkLock();
this.store = store; this.store = store;
@@ -198,7 +210,7 @@ public class NodeEntity implements Node, PermissionCheckValue
return uuid; return uuid;
} }
public void setUuid(String uuid) public synchronized void setUuid(String uuid)
{ {
checkLock(); checkLock();
this.uuid = uuid; this.uuid = uuid;
@@ -210,7 +222,7 @@ public class NodeEntity implements Node, PermissionCheckValue
return typeQNameId; return typeQNameId;
} }
public void setTypeQNameId(Long typeQNameId) public synchronized void setTypeQNameId(Long typeQNameId)
{ {
checkLock(); checkLock();
this.typeQNameId = typeQNameId; this.typeQNameId = typeQNameId;
@@ -222,7 +234,7 @@ public class NodeEntity implements Node, PermissionCheckValue
return localeId; return localeId;
} }
public void setLocaleId(Long localeId) public synchronized void setLocaleId(Long localeId)
{ {
this.localeId = localeId; this.localeId = localeId;
} }
@@ -233,7 +245,7 @@ public class NodeEntity implements Node, PermissionCheckValue
return aclId; return aclId;
} }
public void setAclId(Long aclId) public synchronized void setAclId(Long aclId)
{ {
checkLock(); checkLock();
this.aclId = aclId; this.aclId = aclId;
@@ -245,7 +257,7 @@ public class NodeEntity implements Node, PermissionCheckValue
return deleted; return deleted;
} }
public void setDeleted(Boolean deleted) public synchronized void setDeleted(Boolean deleted)
{ {
checkLock(); checkLock();
this.deleted = deleted; this.deleted = deleted;
@@ -257,7 +269,7 @@ public class NodeEntity implements Node, PermissionCheckValue
return transaction; return transaction;
} }
public void setTransaction(TransactionEntity transaction) public synchronized void setTransaction(TransactionEntity transaction)
{ {
checkLock(); checkLock();
this.transaction = transaction; this.transaction = transaction;
@@ -269,7 +281,7 @@ public class NodeEntity implements Node, PermissionCheckValue
return auditableProperties; return auditableProperties;
} }
public void setAuditableProperties(AuditablePropertiesEntity auditableProperties) public synchronized void setAuditableProperties(AuditablePropertiesEntity auditableProperties)
{ {
checkLock(); checkLock();
this.auditableProperties = auditableProperties; this.auditableProperties = auditableProperties;

View File

@@ -29,6 +29,7 @@ import java.util.List;
public class NodePropertyEntity public class NodePropertyEntity
{ {
private Long nodeId; private Long nodeId;
private Long nodeVersion;
private NodePropertyKey key; private NodePropertyKey key;
private NodePropertyValue value; private NodePropertyValue value;
/** Carries data for queries and updates */ /** Carries data for queries and updates */
@@ -46,7 +47,7 @@ public class NodePropertyEntity
@Override @Override
public String toString() public String toString()
{ {
return "NodePropertyEntity [node=" + nodeId + ", key=" + key + ", value=" + value + "]"; return "NodePropertyEntity [node=" + nodeId + ", nodeVersion=" + nodeVersion + ", key=" + key + ", value=" + value + "]";
} }
public Long getNodeId() public Long getNodeId()
@@ -59,6 +60,16 @@ public class NodePropertyEntity
this.nodeId = nodeId; this.nodeId = nodeId;
} }
public Long getNodeVersion()
{
return nodeVersion;
}
public void setNodeVersion(Long nodeVersion)
{
this.nodeVersion = nodeVersion;
}
public NodePropertyKey getKey() public NodePropertyKey getKey()
{ {
return key; return key;

View File

@@ -0,0 +1,89 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.domain.node;
import java.io.Serializable;
/**
* Key for caches that need to be bound implicitly to the current version of a node.
*
* @author Derek Hulley
* @since 4.0
*/
public class NodeVersionKey implements Serializable
{
private static final long serialVersionUID = 2241045540959490539L;
private final Long nodeId;
private final Long version;
public NodeVersionKey(Long nodeId, Long version)
{
this.nodeId = nodeId;
this.version = version;
}
@Override
public boolean equals(Object other)
{
if (this == other)
{
return true;
}
if (!(other instanceof NodeVersionKey))
{
return false;
}
NodeVersionKey o = (NodeVersionKey)other;
return nodeId.equals(o.nodeId) && version.equals(o.version);
}
@Override
public int hashCode()
{
return nodeId.hashCode() + version.hashCode();
}
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("NodeVersionKey ")
.append("[nodeId=").append(nodeId)
.append(", version=").append(version)
.append("]");
return builder.toString();
}
public Long getNodeId()
{
return nodeId;
}
public Long getVersion()
{
return version;
}
}

View File

@@ -42,6 +42,7 @@ import org.alfresco.repo.domain.node.NodePropertyEntity;
import org.alfresco.repo.domain.node.NodePropertyKey; import org.alfresco.repo.domain.node.NodePropertyKey;
import org.alfresco.repo.domain.node.NodePropertyValue; import org.alfresco.repo.domain.node.NodePropertyValue;
import org.alfresco.repo.domain.node.NodeUpdateEntity; import org.alfresco.repo.domain.node.NodeUpdateEntity;
import org.alfresco.repo.domain.node.NodeVersionKey;
import org.alfresco.repo.domain.node.PrimaryChildrenAclUpdateEntity; import org.alfresco.repo.domain.node.PrimaryChildrenAclUpdateEntity;
import org.alfresco.repo.domain.node.ServerEntity; import org.alfresco.repo.domain.node.ServerEntity;
import org.alfresco.repo.domain.node.StoreEntity; import org.alfresco.repo.domain.node.StoreEntity;
@@ -418,21 +419,23 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
/** /**
* Pull out the key-value pairs from the rows * Pull out the key-value pairs from the rows
*/ */
private Map<Long, Map<NodePropertyKey, NodePropertyValue>> makePersistentPropertiesMap(List<NodePropertyEntity> rows) private Map<NodeVersionKey, Map<NodePropertyKey, NodePropertyValue>> makePersistentPropertiesMap(List<NodePropertyEntity> rows)
{ {
Map<Long, Map<NodePropertyKey, NodePropertyValue>> results = new HashMap<Long, Map<NodePropertyKey, NodePropertyValue>>(3); Map<NodeVersionKey, Map<NodePropertyKey, NodePropertyValue>> results = new HashMap<NodeVersionKey, Map<NodePropertyKey, NodePropertyValue>>(3);
for (NodePropertyEntity row : rows) for (NodePropertyEntity row : rows)
{ {
Long nodeId = row.getNodeId(); Long nodeId = row.getNodeId();
if (nodeId == null) Long nodeVersion = row.getNodeVersion();
if (nodeId == null || nodeVersion == null)
{ {
throw new RuntimeException("Expect results with a Node ID: " + row); throw new RuntimeException("Expect results with a Node and Version: " + row);
} }
Map<NodePropertyKey, NodePropertyValue> props = results.get(nodeId); NodeVersionKey nodeTxnKey = new NodeVersionKey(nodeId, nodeVersion);
Map<NodePropertyKey, NodePropertyValue> props = results.get(nodeTxnKey);
if (props == null) if (props == null)
{ {
props = new HashMap<NodePropertyKey, NodePropertyValue>(17); props = new HashMap<NodePropertyKey, NodePropertyValue>(17);
results.put(nodeId, props); results.put(nodeTxnKey, props);
} }
props.put(row.getKey(), row.getValue()); props.put(row.getKey(), row.getValue());
} }
@@ -460,22 +463,7 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected List<NodeAspectsEntity> selectNodeAspects(Set<Long> nodeIds) protected Map<NodeVersionKey, Map<NodePropertyKey, NodePropertyValue>> selectNodeProperties(Set<Long> nodeIds)
{
if (nodeIds.size() == 0)
{
return Collections.emptyList();
}
NodeAspectsEntity aspects = new NodeAspectsEntity();
aspects.setNodeIds(new ArrayList<Long>(nodeIds));
List<NodeAspectsEntity> rows = (List<NodeAspectsEntity>) template.selectList(SELECT_NODE_ASPECTS, aspects);
return rows;
}
@Override
@SuppressWarnings("unchecked")
protected Map<Long, Map<NodePropertyKey, NodePropertyValue>> selectNodeProperties(Set<Long> nodeIds)
{ {
if (nodeIds.size() == 0) if (nodeIds.size() == 0)
{ {
@@ -488,13 +476,13 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
return makePersistentPropertiesMap(rows); return makePersistentPropertiesMap(rows);
} }
@Override @Override
protected Map<NodePropertyKey, NodePropertyValue> selectNodeProperties(Long nodeId) protected Map<NodeVersionKey, Map<NodePropertyKey, NodePropertyValue>> selectNodeProperties(Long nodeId)
{ {
return selectNodeProperties(nodeId, Collections.<Long>emptySet()); return selectNodeProperties(nodeId, Collections.<Long>emptySet());
} }
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected Map<NodePropertyKey, NodePropertyValue> selectNodeProperties(Long nodeId, Set<Long> qnameIds) protected Map<NodeVersionKey, Map<NodePropertyKey, NodePropertyValue>> selectNodeProperties(Long nodeId, Set<Long> qnameIds)
{ {
NodePropertyEntity prop = new NodePropertyEntity(); NodePropertyEntity prop = new NodePropertyEntity();
// Node // Node
@@ -514,16 +502,7 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
} }
List<NodePropertyEntity> rows = (List<NodePropertyEntity>) template.selectList(SELECT_NODE_PROPERTIES, prop); List<NodePropertyEntity> rows = (List<NodePropertyEntity>) template.selectList(SELECT_NODE_PROPERTIES, prop);
Map<Long, Map<NodePropertyKey, NodePropertyValue>> results = makePersistentPropertiesMap(rows); return makePersistentPropertiesMap(rows);
Map<NodePropertyKey, NodePropertyValue> props = results.get(nodeId);
if (props == null)
{
return Collections.emptyMap();
}
else
{
return props;
}
} }
@Override @Override
@@ -601,19 +580,34 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
} }
} }
@SuppressWarnings("unchecked")
@Override @Override
protected Set<Long> selectNodeAspectIds(Long nodeId) protected Map<NodeVersionKey, Set<QName>> selectNodeAspects(Set<Long> nodeIds)
{ {
Set<Long> aspectIds = new HashSet<Long>(); if (nodeIds.size() == 0)
Set<Long> nodeIds = new HashSet<Long>();
nodeIds.add(nodeId);
List<NodeAspectsEntity> nodeAspectEntities = selectNodeAspects(nodeIds);
if(nodeAspectEntities.size() > 0)
{ {
NodeAspectsEntity nodeAspects = nodeAspectEntities.get(0); return Collections.emptyMap();
aspectIds.addAll(nodeAspects.getAspectQNameIds());
} }
return aspectIds; NodeAspectsEntity aspects = new NodeAspectsEntity();
aspects.setNodeIds(new ArrayList<Long>(nodeIds));
List<NodeAspectsEntity> rows = (List<NodeAspectsEntity>) template.selectList(SELECT_NODE_ASPECTS, aspects);
Map<NodeVersionKey, Set<QName>> results = new HashMap<NodeVersionKey, Set<QName>>(rows.size()*2);
for (NodeAspectsEntity nodeAspectsEntity : rows)
{
Long nodeId = nodeAspectsEntity.getNodeId();
Long nodeVersion = nodeAspectsEntity.getNodeVersion();
NodeVersionKey nodeVersionKey = new NodeVersionKey(nodeId, nodeVersion);
if (results.containsKey(nodeVersionKey))
{
throw new IllegalStateException("Found existing key while querying for node aspects: " + nodeIds);
}
Set<Long> aspectIds = new HashSet<Long>(nodeAspectsEntity.getAspectQNameIds());
Set<QName> aspectQNames = qnameDAO.convertIdsToQNames(aspectIds);
results.put(nodeVersionKey, aspectQNames);
}
return results;
} }
@Override @Override