/* * 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.ArrayList; import java.util.List; import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.cache.lookup.EntityLookupCache; import org.alfresco.repo.cache.lookup.EntityLookupCache.EntityLookupCallbackDAO; import org.alfresco.util.Pair; import org.alfresco.util.ParameterCheck; import org.alfresco.util.SearchLanguageConversion; import org.springframework.dao.ConcurrencyFailureException; /** * Abstract implementation for AVMNodeLinks 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 AbstractAVMNodeLinksDAOImpl implements AVMNodeLinksDAO { private static final String CACHE_REGION_AVM_CHILD_ENTRY = "AVMChildEntry"; private static final String CACHE_REGION_AVM_HISTORY_LINK = "AVMHistoryLink"; private final AVMChildEntryEntityCallbackDAO avmChildEntryEntityDaoCallback; private final AVMHistoryLinkEntityCallbackDAO avmHistoryLinkEntityDaoCallback; /** * Cache for the AVM child entry entity:
* KEY: ChildKey
* VALUE: AVMChildEntryEntity
* VALUE KEY: Pair of node IDs (parent, child)
*/ private EntityLookupCache> avmChildEntryCache; /** * Cache for the AVM history link entity:
* KEY: Descendent ID
* VALUE: AVMHistoryLinkEntity
* VALUE KEY: AVMHistoryLinkEntity
*/ private EntityLookupCache avmHistoryLinkCache; /** * Set the cache to use for avm_child_entry lookups (optional). * * @param avmChildEntryCache the cache of IDs to AVMChildEntryEntities */ public void setAvmChildEntryCache(SimpleCache avmChildEntryCache) { this.avmChildEntryCache = new EntityLookupCache>( avmChildEntryCache, CACHE_REGION_AVM_CHILD_ENTRY, avmChildEntryEntityDaoCallback); } /** * Set the cache to use for avm_history_link lookups (optional). * * @param avmHistoryLinkCache the cache of ID to ID (from descendent to ancestor) */ public void setAvmHistoryLinkCache(SimpleCache avmHistoryLinkCache) { this.avmHistoryLinkCache = new EntityLookupCache( avmHistoryLinkCache, CACHE_REGION_AVM_HISTORY_LINK, avmHistoryLinkEntityDaoCallback); } /** * 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. */ public AbstractAVMNodeLinksDAOImpl() { this.avmChildEntryEntityDaoCallback = new AVMChildEntryEntityCallbackDAO(); this.avmChildEntryCache = new EntityLookupCache>(avmChildEntryEntityDaoCallback); this.avmHistoryLinkEntityDaoCallback = new AVMHistoryLinkEntityCallbackDAO(); this.avmHistoryLinkCache = new EntityLookupCache(avmHistoryLinkEntityDaoCallback); } /** * {@inheritDoc} */ public void createChildEntry(long parentNodeId, String name, long childNodeId) { ParameterCheck.mandatory("name", name); AVMChildEntryEntity ceEntity = new AVMChildEntryEntity(); ceEntity.setParentNodeId(parentNodeId); ceEntity.setName(name); ceEntity.setChildNodeId(childNodeId); Pair entityPair = avmChildEntryCache.getOrCreateByValue(ceEntity); entityPair.getSecond(); } /** * {@inheritDoc} */ public AVMChildEntryEntity getChildEntry(long parentNodeId, String name) { ParameterCheck.mandatory("name", name); Pair entityPair = avmChildEntryCache.getByKey(new ChildKey(parentNodeId, name)); if (entityPair == null) { return null; } return entityPair.getSecond(); } /** * {@inheritDoc} */ public List getChildEntriesByParent(long parentNodeId, String childNamePattern) { List result = null; if ((childNamePattern == null) || (childNamePattern.length() == 0)) { result = getChildEntryEntitiesByParent(parentNodeId); } else { String pattern = SearchLanguageConversion.convert(SearchLanguageConversion.DEF_LUCENE, SearchLanguageConversion.DEF_SQL_LIKE, childNamePattern); result = getChildEntryEntitiesByParent(parentNodeId, pattern); } if (result == null) { result = new ArrayList(0); } return result; } /** * {@inheritDoc} */ public AVMChildEntryEntity getChildEntry(long parentNodeId, long childNodeId) { AVMChildEntryEntity ceEntity = new AVMChildEntryEntity(); ceEntity.setParentNodeId(parentNodeId); ceEntity.setChildNodeId(childNodeId); Pair entityPair = avmChildEntryCache.getByValue(ceEntity); if (entityPair == null) { return null; } return entityPair.getSecond(); } /** * {@inheritDoc} */ public List getChildEntriesByChild(long childNodeId) { List result = getChildEntryEntitiesByChild(childNodeId); if (result == null) { result = new ArrayList(0); } return result; } /** * {@inheritDoc} */ public void deleteChildEntry(AVMChildEntryEntity childEntryEntity) { ParameterCheck.mandatory("childEntryEntity", childEntryEntity); ParameterCheck.mandatory("childEntryEntity.getParentNodeId()", childEntryEntity.getParentNodeId()); ParameterCheck.mandatory("childEntryEntity.getName()", childEntryEntity.getName()); ChildKey key = new ChildKey(childEntryEntity.getParentNodeId(), childEntryEntity.getName()); Pair entityPair = avmChildEntryCache.getByKey(key); if (entityPair == null) { return; } int deleted = avmChildEntryCache.deleteByKey(key); if (deleted < 1) { throw new ConcurrencyFailureException("AVMChildEntry for parent/name (" + key.getParentNodeId() + ", " + key.getName() + ") no longer exists"); } } /** * {@inheritDoc} */ public void deleteChildEntriesByParent(long parentNodeId) { List ceEntities = getChildEntriesByParent(parentNodeId, null); if (ceEntities.size() == 0) { return; } for (AVMChildEntryEntity ceEntity : ceEntities) { deleteChildEntry(ceEntity); } // TODO single delete + cache(s) /* int deleted = deleteChildEntryEntities(parentNodeId); if (deleted < 1) { throw new ConcurrencyFailureException("AVMChildEntries for parent node ID " + parentNodeId + " no longer exist"); } // TODO clear child entry cache for this parent node id */ } private class ChildKey implements Serializable { private static final long serialVersionUID = 848161072437569305L; /** * The Parent Node Id */ private Long parentNodeId; /** * The child's name. */ private String name; public ChildKey(Long parentNodeId, String name) { this.parentNodeId = parentNodeId; this.name = name; } public Long getParentNodeId() { return parentNodeId; } public String getName() { return name; } /** * Override of equals. */ @Override public boolean equals(Object other) { if (this == other) { return true; } if (!(other instanceof ChildKey)) { return false; } ChildKey o = (ChildKey)other; return parentNodeId.equals(o.getParentNodeId()) && name.equalsIgnoreCase(o.getName()); } /** * Override of hashCode. */ public int hashCode() { return parentNodeId.hashCode() + name.toLowerCase().hashCode(); } } /** * Callback for avm_child_entry DAO */ private class AVMChildEntryEntityCallbackDAO implements EntityLookupCallbackDAO> { private final Pair convertEntityToPair(AVMChildEntryEntity ceEntity) { if (ceEntity == null) { return null; } else { return new Pair(new ChildKey(ceEntity.getParentNodeId(), ceEntity.getName()), ceEntity); } } public Pair getValueKey(AVMChildEntryEntity value) { return new Pair(value.getParentNodeId(), value.getChildId()); } public Pair createValue(AVMChildEntryEntity value) { createChildEntryEntity(value); return convertEntityToPair(value); } public Pair findByKey(ChildKey key) { AVMChildEntryEntity entity = getChildEntryEntity(key.getParentNodeId(), key.getName()); return convertEntityToPair(entity); } public Pair findByValue(AVMChildEntryEntity value) { AVMChildEntryEntity entity = getChildEntryEntity(value.getParentNodeId(), value.getChildId()); return convertEntityToPair(entity); } public int updateValue(ChildKey key, AVMChildEntryEntity value) { throw new UnsupportedOperationException("updateValue(Long, AVMChildEntryEntity"); } public int deleteByKey(ChildKey key) { return deleteChildEntryEntity(key.getParentNodeId(), key.getName()); } public int deleteByValue(AVMChildEntryEntity value) { return deleteChildEntryEntity(value.getParentNodeId(), value.getChildId()); } } protected abstract List getChildEntryEntitiesByParent(long parentNodeId); protected abstract List getChildEntryEntitiesByParent(long parentNodeId, String childNamePattern); protected abstract List getChildEntryEntitiesByChild(long childNodeId); protected abstract AVMChildEntryEntity getChildEntryEntity(long parentNodeId, String name); protected abstract AVMChildEntryEntity getChildEntryEntity(long parentNodeId, long childNodeId); protected abstract AVMChildEntryEntity getChildEntryEntity(AVMChildEntryEntity childEntryEntity); protected abstract void createChildEntryEntity(AVMChildEntryEntity childEntryEntity); protected abstract int deleteChildEntryEntity(long parentNodeId, String name); protected abstract int deleteChildEntryEntity(long parentNodeId, long childNodeId); protected abstract int deleteChildEntryEntities(long parentNodeId); /** * {@inheritDoc} */ public void createMergeLink(long mergeFromNodeId, long mergeToNodeId) { createMergeLinkEntity(mergeFromNodeId, mergeToNodeId); } /** * {@inheritDoc} */ public void deleteMergeLink(long mergeFromNodeId, long mergeToNodeId) { AVMMergeLinkEntity mlEntity = getMergeLinkByTo(mergeToNodeId); if (mlEntity == null) { return; } int deleted = deleteMergeLinkEntity(mergeFromNodeId, mergeToNodeId); if (deleted < 1) { throw new ConcurrencyFailureException("AVMMergeLink (" + mergeFromNodeId + ", " + mergeToNodeId + ") no longer exists"); } } /** * {@inheritDoc} */ public AVMMergeLinkEntity getMergeLinkByTo(long mergeToNodeId) { return getMergeLinkEntityByTo(mergeToNodeId); } /** * {@inheritDoc} */ public List getMergeLinksByFrom(long mergeFromNodeId) { return getMergeLinkEntitiesByFrom(mergeFromNodeId); } protected abstract void createMergeLinkEntity(long mergeFromNodeId, long mergeToNodeId); protected abstract int deleteMergeLinkEntity(long mergeFromNodeId, long mergeToNodeId); protected abstract AVMMergeLinkEntity getMergeLinkEntityByTo(long mergeToNodeId); protected abstract List getMergeLinkEntitiesByFrom(long mergeFromNodeId); /** * {@inheritDoc} */ public void createHistoryLink(long ancestorNodeId, long descendentNodeId) { AVMHistoryLinkEntity hlEntity = new AVMHistoryLinkEntity(ancestorNodeId, descendentNodeId); avmHistoryLinkCache.getOrCreateByValue(hlEntity); // ignore return value } /** * {@inheritDoc} */ public void deleteHistoryLink(long ancestorNodeId, long descendentNodeId) { AVMHistoryLinkEntity hlEntity = new AVMHistoryLinkEntity(ancestorNodeId, descendentNodeId); Pair entityPair = avmHistoryLinkCache.getByValue(hlEntity); if (entityPair == null) { return; } int deleted = avmHistoryLinkCache.deleteByValue(hlEntity); if (deleted < 1) { throw new ConcurrencyFailureException("AVMHistoryLinkEntity (" + ancestorNodeId + ", " + descendentNodeId + ") no longer exists"); } } /** * {@inheritDoc} */ public AVMHistoryLinkEntity getHistoryLinkByDescendent(long descendentNodeId) { Pair entityPair = avmHistoryLinkCache.getByKey(descendentNodeId); if (entityPair == null) { return null; } return entityPair.getSecond(); } /** * {@inheritDoc} */ public List getHistoryLinksByAncestor(long ancestorNodeId) { // not via cache return getHistoryLinkEntitiesByAncestor(ancestorNodeId); } /** * Callback for avm_history_link DAO */ private class AVMHistoryLinkEntityCallbackDAO implements EntityLookupCallbackDAO { private final Pair convertEntityToPair(AVMHistoryLinkEntity hlEntity) { if (hlEntity == null) { return null; } else { return new Pair(hlEntity.getDescendentNodeId(), hlEntity); } } public AVMHistoryLinkEntity getValueKey(AVMHistoryLinkEntity value) { return value; } public Pair createValue(AVMHistoryLinkEntity value) { createHistoryLinkEntity(value.getAncestorNodeId(), value.getDescendentNodeId()); return convertEntityToPair(value); } public Pair findByKey(Long key) { AVMHistoryLinkEntity entity = getHistoryLinkEntityByDescendent(key); return convertEntityToPair(entity); } public Pair findByValue(AVMHistoryLinkEntity value) { AVMHistoryLinkEntity entity = getHistoryLinkEntity(value.getAncestorNodeId(), value.getDescendentNodeId()); return convertEntityToPair(entity); } public int updateValue(Long key, AVMHistoryLinkEntity value) { throw new UnsupportedOperationException("updateValue(Long, AVMHistoryLinkEntity"); } public int deleteByKey(Long key) { AVMHistoryLinkEntity entity = getHistoryLinkEntityByDescendent(key); return deleteHistoryLinkEntity(entity.getAncestorNodeId(), entity.getDescendentNodeId()); } public int deleteByValue(AVMHistoryLinkEntity value) { return deleteHistoryLinkEntity(value.getAncestorNodeId(), value.getDescendentNodeId()); } } protected abstract void createHistoryLinkEntity(long ancestorNodeId, long descendentNodeId); protected abstract int deleteHistoryLinkEntity(long ancestorNodeId, long descendentNodeId); protected abstract AVMHistoryLinkEntity getHistoryLinkEntity(long ancestorNodeId, long descendentNodeId); protected abstract AVMHistoryLinkEntity getHistoryLinkEntityByDescendent(long descendentNodeId); protected abstract List getHistoryLinkEntitiesByAncestor(long ancestorNodeId); }