/* * Copyright (C) 2005-2009 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.avm; import java.io.Serializable; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.alfresco.repo.cache.NullCache; import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.cache.lookup.EntityLookupCache; import org.alfresco.repo.cache.lookup.EntityLookupCache.EntityLookupCallbackDAO; import org.alfresco.repo.domain.PropertyValue; import org.alfresco.repo.domain.QNameDAO; import org.alfresco.service.namespace.QName; import org.alfresco.util.Pair; import org.alfresco.util.ParameterCheck; import org.springframework.dao.ConcurrencyFailureException; /** * Abstract implementation for AVMNode DAO. *

* This provides basic services such as caching but defers to the underlying implementation * for CRUD operations. * * @author janv * @since 3.2 */ public abstract class AbstractAVMNodeDAOImpl implements AVMNodeDAO { private static final String CACHE_REGION_AVM_NODE = "AVMNode"; private static final String CACHE_REGION_AVM_NODE_PROP = "AVMNodeProp"; private final AVMNodeEntityCallbackDAO avmNodeEntityDaoCallback; private final AVMNodePropertyEntityCallbackDAO avmNodePropEntityDaoCallback; private QNameDAO qnameDAO; /** * Cache for the AVM node entity:
* KEY: ID (node)
* VALUE: AVMNodeEntity
* VALUE KEY: None
*/ private EntityLookupCache avmNodeCache; /** * Cache for the AVM node property entity:
* KEY: Pair of IDs (node, qname)
* VALUE: AVMNodePropertyEntity
* VALUE KEY: None
*/ private EntityLookupCache, AVMNodePropertyEntity, Serializable> avmNodePropCache; /** * Set the cache to use for avm_aspects lookups (optional). * * @param avmNodeAspectsCache */ private SimpleCache avmNodeAspectsCache; /** * Set the cache to use for avm_nodes lookups (optional). * * @param avmNodeCache the cache of IDs to AVMNodeEntities */ public void setAvmNodeCache(SimpleCache avmNodeCache) { this.avmNodeCache = new EntityLookupCache( avmNodeCache, CACHE_REGION_AVM_NODE, avmNodeEntityDaoCallback); } /** * Set the cache to use for avm_node_properties lookups (optional). * * @param avmNodePropCache the cache of IDs to AVMNodePropertyEntities */ public void setAvmNodePropertyCache(SimpleCache avmNodePropCache) { this.avmNodePropCache = new EntityLookupCache, AVMNodePropertyEntity, Serializable>( avmNodePropCache, CACHE_REGION_AVM_NODE_PROP, avmNodePropEntityDaoCallback); } /** * Set the cache to use for avm_aspects lookups (optional). * * @param avmNodeAspectsCache */ public void setAvmNodeAspectsCache(SimpleCache avmNodeAspectsCache) { this.avmNodeAspectsCache = avmNodeAspectsCache; } public void setQnameDAO(QNameDAO qnameDAO) { this.qnameDAO = qnameDAO; } /** * Default constructor. *

* This sets up the DAO accessors to bypass any caching to handle the case where the caches are not * supplied in the setters. */ @SuppressWarnings("unchecked") public AbstractAVMNodeDAOImpl() { this.avmNodeEntityDaoCallback = new AVMNodeEntityCallbackDAO(); this.avmNodeCache = new EntityLookupCache(avmNodeEntityDaoCallback); this.avmNodePropEntityDaoCallback = new AVMNodePropertyEntityCallbackDAO(); this.avmNodePropCache = new EntityLookupCache, AVMNodePropertyEntity, Serializable>(avmNodePropEntityDaoCallback); this.avmNodeAspectsCache = (SimpleCache)new NullCache(); } public AVMNodeEntity createNode(AVMNodeEntity nodeEntity) { ParameterCheck.mandatory("nodeEntity", nodeEntity); nodeEntity.setVers(0L); Pair entityPair = avmNodeCache.getOrCreateByValue(nodeEntity); return entityPair.getSecond(); } /** * {@inheritDoc} */ public AVMNodeEntity getNode(long nodeId) { Pair entityPair = avmNodeCache.getByKey(nodeId); if (entityPair == null) { return null; } return entityPair.getSecond(); } /** * {@inheritDoc} */ public void clearNodeEntityCache() { avmNodeCache.clear(); } /** * {@inheritDoc} */ public void updateNode(AVMNodeEntity nodeEntity) { ParameterCheck.mandatory("nodeEntity", nodeEntity); ParameterCheck.mandatory("nodeEntity.getId()", nodeEntity.getId()); ParameterCheck.mandatory("nodeEntity.getVers()", nodeEntity.getVers()); int updated = avmNodeCache.updateValue(nodeEntity.getId(), nodeEntity); if (updated < 1) { throw new ConcurrencyFailureException("AVMNode with ID (" + nodeEntity.getId() + ") no longer exists or has been updated concurrently"); } nodeEntity.setVers(nodeEntity.getVers()+1); } /** * {@inheritDoc} * @deprecated */ public void updateNodeModTimeAndGuid(AVMNodeEntity nodeEntity) { ParameterCheck.mandatory("nodeEntity", nodeEntity); ParameterCheck.mandatory("nodeEntity.getId()", nodeEntity.getId()); ParameterCheck.mandatory("nodeEntity.getGuid()", nodeEntity.getGuid()); ParameterCheck.mandatory("nodeEntity.getModifiedDate()", nodeEntity.getModifiedDate()); ParameterCheck.mandatory("nodeEntity.getVers()", nodeEntity.getVers()); int updated = updateNodeEntityModTimeAndGuid(nodeEntity); if (updated < 1) { throw new ConcurrencyFailureException("AVMNode with ID (" + nodeEntity.getId() + ") no longer exists or has been updated concurrently"); } nodeEntity.setVers(nodeEntity.getVers()+1); // update cache avmNodeCache.removeByKey(nodeEntity.getId()); avmNodeCache.getByKey(nodeEntity.getId()); } /** * {@inheritDoc} * @deprecated */ public void updateNodeModTimeAndContentData(AVMNodeEntity nodeEntity) { ParameterCheck.mandatory("nodeEntity", nodeEntity); ParameterCheck.mandatory("nodeEntity.getId()", nodeEntity.getId()); ParameterCheck.mandatory("nodeEntity.getModifiedDate()", nodeEntity.getModifiedDate()); ParameterCheck.mandatory("nodeEntity.getVers()", nodeEntity.getVers()); int updated = updateNodeEntityModTimeAndContentData(nodeEntity); if (updated < 1) { throw new ConcurrencyFailureException("AVMNode with ID (" + nodeEntity.getId() + ") no longer exists or has been updated concurrently"); } nodeEntity.setVers(nodeEntity.getVers()+1); // update cache avmNodeCache.removeByKey(nodeEntity.getId()); avmNodeCache.getByKey(nodeEntity.getId()); } /** * {@inheritDoc} */ public List getNodesNewInStore(long storeId) { return getNodeEntitiesNewInStore(storeId); } /** * {@inheritDoc} */ public List getLayeredNodesNewInStore(long storeId) { return getLayeredNodeEntitiesNewInStore(storeId); } /** * {@inheritDoc} */ public List getLayeredNodesNewInStoreIDs(long storeId) { return getLayeredNodeEntityIdsNewInStore(storeId); } /** * {@inheritDoc} */ public List getNodeOrphans() { return getNodeEntityOrphans(); } /** * {@inheritDoc} */ public void updateNodesClearNewInStore(long storeId) { updateNodeEntitiesClearNewInStore(storeId); } /** * {@inheritDoc} */ public void deleteNode(long nodeId) { Pair entityPair = avmNodeCache.getByKey(nodeId); if (entityPair == null) { return; } int deleted = avmNodeCache.deleteByKey(nodeId); if (deleted < 1) { throw new ConcurrencyFailureException("AVMNode with ID " + nodeId + " no longer exists"); } } /** * {@inheritDoc} */ public List getAllLayeredDirectories() { return getAllLayeredDirectoryNodeEntities(); } /** * {@inheritDoc} */ public List getAllLayeredFiles() { return getAllLayeredFileNodeEntities(); } /** * {@inheritDoc} */ public List getAVMNodesByAclId(long aclId) { return getAVMNodeEntityIdsByAclId(aclId); } /** * {@inheritDoc} */ public void getContentUrls(ContentUrlHandler handler) { getPlainFileContentUrls(handler); } /** * Callback for avm_nodes DAO */ private class AVMNodeEntityCallbackDAO implements EntityLookupCallbackDAO { private final Pair convertEntityToPair(AVMNodeEntity nodeEntity) { if (nodeEntity == null) { return null; } else { return new Pair(nodeEntity.getId(), nodeEntity); } } public Serializable getValueKey(AVMNodeEntity value) { return null; } public Pair createValue(AVMNodeEntity value) { AVMNodeEntity entity = createNodeEntity(value); return convertEntityToPair(entity); } public Pair findByKey(Long key) { AVMNodeEntity entity = getNodeEntity(key); return convertEntityToPair(entity); } public Pair findByValue(AVMNodeEntity value) { if ((value != null) && (value.getId() != null)) { return findByKey(value.getId()); } return null; } public int updateValue(Long key, AVMNodeEntity value) { return updateNodeEntity(value); } public int deleteByKey(Long key) { return deleteNodeEntity(key); } public int deleteByValue(AVMNodeEntity value) { // TODO throw new UnsupportedOperationException("deleteByValue(AVMNodeEntity)"); } } protected abstract AVMNodeEntity createNodeEntity(AVMNodeEntity nodeEntity); protected abstract AVMNodeEntity getNodeEntity(long nodeId); protected abstract int updateNodeEntity(AVMNodeEntity nodeEntity); protected abstract int updateNodeEntityModTimeAndGuid(AVMNodeEntity nodeEntity); protected abstract int updateNodeEntityModTimeAndContentData(AVMNodeEntity nodeEntity); protected abstract int deleteNodeEntity(long nodeId); protected abstract void updateNodeEntitiesClearNewInStore(long storeId); protected abstract List getNodeEntitiesNewInStore(long storeId); protected abstract List getLayeredNodeEntitiesNewInStore(long storeId); protected abstract List getLayeredNodeEntityIdsNewInStore(long storeId); protected abstract List getNodeEntityOrphans(); protected abstract List getAllLayeredDirectoryNodeEntities(); protected abstract List getAllLayeredFileNodeEntities(); protected abstract List getAVMNodeEntityIdsByAclId(long aclId); protected abstract void getPlainFileContentUrls(ContentUrlHandler handler); /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public Set getAspects(long nodeId) { Set aspects = (Set)avmNodeAspectsCache.get(nodeId); if (aspects != null) { return aspects; } Set aspectQNames = null; // Get it from the DB List aspectIds = getAspectEntities(nodeId); if (aspectIds != null) { // Convert to QNames aspectQNames = qnameDAO.convertIdsToQNames(new HashSet(aspectIds)); } else { aspectQNames = new HashSet(0); } // Cache it avmNodeAspectsCache.put(nodeId, aspectQNames); return aspectQNames; } /** * {@inheritDoc} */ public void createAspect(long nodeId, QName qname) { Set aspects = getAspects(nodeId); if (aspects.contains(qname)) { return; } // Get the persistent ID for the QName Pair qnamePair = qnameDAO.getQName(qname); if (qnamePair != null) { Long qnameId = qnamePair.getFirst(); createAspectEntity(nodeId, qnameId); // Cache it aspects.add(qname); avmNodeAspectsCache.put(new Long(nodeId), aspects); } } /** * {@inheritDoc} */ public void deleteAspect(long nodeId, QName qname) { Set aspects = getAspects(nodeId); if (! aspects.contains(qname)) { return; } // Get the persistent ID for the QName Pair qnamePair = qnameDAO.getQName(qname); if (qnamePair != null) { Long qnameId = qnamePair.getFirst(); int deleted = deleteAspectEntity(nodeId, qnameId); if (deleted < 1) { throw new ConcurrencyFailureException("AVMNodeAspect (" + nodeId + ", " + qnameId + ") no longer exists"); } // Remove from cache aspects.remove(qname); avmNodeAspectsCache.put(new Long(nodeId), aspects); } } /** * {@inheritDoc} */ public void deleteAspects(long nodeId) { Set naEntities = getAspects(nodeId); if (naEntities.size() == 0) { return; } int deleted = deleteAspectEntities(nodeId); if (deleted < 1) { throw new ConcurrencyFailureException("AVMNodeAspects for node ID " + nodeId + " no longer exist"); } // Remove from cache avmNodeAspectsCache.remove(nodeId); } protected abstract List getAspectEntities(long nodeId); protected abstract void createAspectEntity(long nodeId, long qnameId); protected abstract int deleteAspectEntity(long nodeId, long qnameId); protected abstract int deleteAspectEntities(long nodeId); /** * {@inheritDoc} */ public void createOrUpdateNodeProperty(long nodeId, QName qname, PropertyValue value) { ParameterCheck.mandatory("qname", qname); // Get the persistent ID for the QName Pair qnamePair = qnameDAO.getOrCreateQName(qname); Long qnameId = qnamePair.getFirst(); AVMNodePropertyEntity propEntity = new AVMNodePropertyEntity(nodeId, qnameId, value); Pair key = new Pair(nodeId, propEntity.getQnameId()); Pair, AVMNodePropertyEntity> entityPair = avmNodePropCache.getByKey(key); if (entityPair != null) { int updated = avmNodePropCache.updateValue(key, propEntity); if (updated < 1) { throw new ConcurrencyFailureException("AVMNodePropertyEntity with key (" + propEntity.getNodeId() + ", " + propEntity.getQnameId() + ") no longer exists"); } } else { avmNodePropCache.getOrCreateByValue(propEntity); } } /** * {@inheritDoc} */ /* public AVMNodePropertyEntity getNodeProperty(long nodeId, QName qname) { // Get the persistent ID for the QName Pair qnamePair = qnameDAO.getQName(qname); if (qnamePair != null) { Long qnameId = qnamePair.getFirst(); Pair key = new Pair(nodeId, qnameId); Pair, AVMNodePropertyEntity> entityPair = avmNodePropCache.getByKey(key); if (entityPair == null) { return null; } return entityPair.getSecond(); } } */ /** * {@inheritDoc} */ public Map getNodeProperties(long nodeId) { // TODO not via cache List npEntities = getNodePropertyEntities(nodeId); Map nProps = new HashMap(npEntities.size()); for (AVMNodePropertyEntity npEntity : npEntities) { Pair qnamePair = qnameDAO.getQName(npEntity.getQnameId()); if (qnamePair != null) { nProps.put(qnamePair.getSecond(), npEntity); } } return nProps; } /** * {@inheritDoc} */ public void deleteNodeProperty(long nodeId, QName qname) { // Get the persistent ID for the QName Pair qnamePair = qnameDAO.getQName(qname); if (qnamePair != null) { Long qnameId = qnamePair.getFirst(); Pair key = new Pair(nodeId, qnameId); Pair, AVMNodePropertyEntity> entityPair = avmNodePropCache.getByKey(key); if (entityPair == null) { return; } int deleted = avmNodePropCache.deleteByKey(key); if (deleted < 1) { throw new ConcurrencyFailureException("AVMNodeProperty (" + nodeId + ", " + qnameId + ") no longer exists"); } } } /** * {@inheritDoc} */ public void deleteNodeProperties(long nodeId) { Map nProps = getNodeProperties(nodeId); if (nProps.size() == 0) { return; } for (QName propQName : nProps.keySet()) { deleteNodeProperty(nodeId, propQName); } // TODO single delete + cache(s) /* int deleted = deleteNodePropertyEntities(nodeId); if (deleted < 1) { throw new ConcurrencyFailureException("AVMNodeProperties for node ID " + nodeId + " no longer exist"); } // TODO clear node property cache for this node id */ } /** * Callback for avm_node_properties DAO */ private class AVMNodePropertyEntityCallbackDAO implements EntityLookupCallbackDAO, AVMNodePropertyEntity, Serializable> { private final Pair, AVMNodePropertyEntity> convertEntityToPair(AVMNodePropertyEntity nodePropEntity) { if (nodePropEntity == null) { return null; } else { Pair key = new Pair(nodePropEntity.getNodeId(), nodePropEntity.getQnameId()); return new Pair, AVMNodePropertyEntity>(key, nodePropEntity); } } public Serializable getValueKey(AVMNodePropertyEntity value) { return null; } public Pair, AVMNodePropertyEntity> createValue(AVMNodePropertyEntity value) { insertNodePropertyEntity(value); return convertEntityToPair(value); } public Pair, AVMNodePropertyEntity> findByKey(Pair key) { AVMNodePropertyEntity entity = getNodePropertyEntity(key.getFirst(), key.getSecond()); return convertEntityToPair(entity); } public Pair, AVMNodePropertyEntity> findByValue(AVMNodePropertyEntity value) { if ((value.getNodeId() != null) && (value.getQnameId() != null)) { return findByKey(new Pair(value.getNodeId(), value.getQnameId())); } return null; } public int updateValue(Pair key, AVMNodePropertyEntity value) { return updateNodePropertyEntity(value); } public int deleteByKey(Pair key) { return deleteNodePropertyEntity(key.getFirst(), key.getSecond()); } public int deleteByValue(AVMNodePropertyEntity value) { throw new UnsupportedOperationException("deleteByValue(AVMNodePropertyEntity)"); } } protected abstract void insertNodePropertyEntity(AVMNodePropertyEntity propEntity); protected abstract int updateNodePropertyEntity(AVMNodePropertyEntity propEntity); protected abstract AVMNodePropertyEntity getNodePropertyEntity(long nodeId, long qnameId); protected abstract List getNodePropertyEntities(long nodeId); protected abstract int deleteNodePropertyEntity(long nodeId, long qnameId); protected abstract int deleteNodePropertyEntities(long nodeId); }