/* * Copyright (C) 2005-2010 Alfresco Software Limited. * * This file is part of Alfresco * * Alfresco is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Alfresco 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see . */ package org.alfresco.repo.domain.qname; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.cache.lookup.EntityLookupCache; import org.alfresco.repo.cache.lookup.EntityLookupCache.EntityLookupCallbackDAOAdaptor; import org.alfresco.service.namespace.QName; import org.springframework.dao.ConcurrencyFailureException; import org.springframework.dao.DataIntegrityViolationException; import org.alfresco.util.Pair; import org.springframework.extensions.surf.util.ParameterCheck; /** * Abstract implementation of the QName and Namespace DAO interface. * * @author Derek Hulley * @since 3.4 */ public abstract class AbstractQNameDAOImpl implements QNameDAO { private static final String CACHE_REGION_NAMESPACE = "Namespace"; private static final String CACHE_REGION_QNAME = "QName"; /** * Cache for the Namespace values:
* KEY: ID
* VALUE: Namespace
* VALUE KEY: Namespace
*/ private EntityLookupCache namespaceCache; /** * Cache for the QName values:
* KEY: ID
* VALUE: QName
* VALUE KEY: QName
*/ private EntityLookupCache qnameCache; /** * 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. */ protected AbstractQNameDAOImpl() { this.namespaceCache = new EntityLookupCache(new NamespaceCallbackDAO()); this.qnameCache = new EntityLookupCache(new QNameCallbackDAO()); } /** * Set the cache that maintains the ID-Namespace mappings and vice-versa. * * @param namespaceCache the cache */ public void setNamespaceCache(SimpleCache namespaceCache) { this.namespaceCache = new EntityLookupCache( namespaceCache, CACHE_REGION_NAMESPACE, new NamespaceCallbackDAO()); } /** * Set the cache that maintains the ID-Namespace mappings and vice-versa. * * @param qnameCache the cache */ public void setQnameCache(SimpleCache qnameCache) { this.qnameCache = new EntityLookupCache( qnameCache, CACHE_REGION_QNAME, new QNameCallbackDAO()); } //================================ // 'alf_namespace' accessors //================================ public Pair getNamespace(Long id) { if (id == null) { throw new IllegalArgumentException("Cannot look up entity by null ID."); } Pair entityPair = namespaceCache.getByKey(id); if (entityPair == null) { throw new DataIntegrityViolationException("No namespace exists for ID " + id); } return entityPair; } public Pair getNamespace(String namespaceUri) { if (namespaceUri == null) { throw new IllegalArgumentException("Namespace URI cannot be null"); } Pair entityPair = namespaceCache.getByValue(namespaceUri); return entityPair; } public Pair getOrCreateNamespace(String namespaceUri) { if (namespaceUri == null) { throw new IllegalArgumentException("Namespace URI cannot be null"); } Pair entityPair = namespaceCache.getOrCreateByValue(namespaceUri); return entityPair; } public void updateNamespace(String oldNamespaceUri, String newNamespaceUri) { ParameterCheck.mandatory("newNamespaceUri", newNamespaceUri); Pair oldEntityPair = getNamespace(oldNamespaceUri); // incl. null check if (oldEntityPair == null) { throw new DataIntegrityViolationException( "Cannot update namespace as it doesn't exist: " + oldNamespaceUri); } // Find the value int updated = namespaceCache.updateValue(oldEntityPair.getFirst(), newNamespaceUri); if (updated != 1) { throw new ConcurrencyFailureException( "Incorrect update count: \n" + " Namespace: " + oldNamespaceUri + "\n" + " Rows Updated: " + updated); } // All the QNames need to be dumped qnameCache.clear(); // Done } /** * Callback for alf_namespace DAO. */ private class NamespaceCallbackDAO extends EntityLookupCallbackDAOAdaptor { @Override public String getValueKey(String value) { return value; } public Pair findByKey(Long id) { NamespaceEntity entity = findNamespaceEntityById(id); if (entity == null) { return null; } else { return new Pair(id, entity.getUriSafe()); } } @Override public Pair findByValue(String uri) { NamespaceEntity entity = findNamespaceEntityByUri(uri); if (entity == null) { return null; } else { return new Pair(entity.getId(), uri); } } public Pair createValue(String uri) { NamespaceEntity entity = createNamespaceEntity(uri); return new Pair(entity.getId(), uri); } @Override public int updateValue(Long id, String uri) { NamespaceEntity entity = findNamespaceEntityById(id); if (entity == null) { // Client can decide if this is a problem return 0; } return updateNamespaceEntity(entity, uri); } } protected abstract NamespaceEntity findNamespaceEntityById(Long id); protected abstract NamespaceEntity findNamespaceEntityByUri(String uri); protected abstract NamespaceEntity createNamespaceEntity(String uri); protected abstract int updateNamespaceEntity(NamespaceEntity entity, String uri); //================================ // 'alf_qname' accessors //================================ public Pair getQName(Long id) { if (id == null) { throw new IllegalArgumentException("Cannot look up entity by null ID."); } Pair entityPair = qnameCache.getByKey(id); if (entityPair == null) { throw new DataIntegrityViolationException("No qname exists for ID " + id); } return entityPair; } public Pair getQName(final QName qname) { if (qname == null) { throw new IllegalArgumentException("QName cannot be null"); } Pair entityPair = qnameCache.getByValue(qname); return entityPair; } public Pair getOrCreateQName(QName qname) { if (qname == null) { throw new IllegalArgumentException("QName cannot be null"); } Pair entityPair = qnameCache.getOrCreateByValue(qname); return entityPair; } public Pair updateQName(QName qnameOld, QName qnameNew) { if (qnameOld == null|| qnameNew == null) { throw new IllegalArgumentException("QName cannot be null"); } if (qnameOld.equals(qnameNew)) { throw new IllegalArgumentException("Cannot update QNames: they are the same"); } // See if the old QName exists Pair qnameOldPair = qnameCache.getByValue(qnameOld); if (qnameOldPair == null) { throw new IllegalArgumentException("Cannot rename QName. QName " + qnameOld + " does not exist"); } // See if the new QName exists if (qnameCache.getByValue(qnameNew) != null) { throw new IllegalArgumentException("Cannot rename QName. QName " + qnameNew + " already exists"); } // Update Long qnameId = qnameOldPair.getFirst(); int updated = qnameCache.updateValue(qnameId, qnameNew); if (updated != 1) { throw new ConcurrencyFailureException("Failed to update QName entity " + qnameId); } return new Pair(qnameId, qnameNew); } /** * Callback for alf_qname DAO. */ private class QNameCallbackDAO extends EntityLookupCallbackDAOAdaptor { @Override public QName getValueKey(QName value) { return value; } public Pair findByKey(Long id) { QNameEntity entity = findQNameEntityById(id); if (entity == null) { return null; } else { Long namespaceId = entity.getNamespaceId(); String uri = getNamespace(namespaceId).getSecond(); String localName = entity.getLocalNameSafe(); QName qname = QName.createQName(uri, localName); return new Pair(id, qname); } } @Override public Pair findByValue(QName qname) { String uri = qname.getNamespaceURI(); String localName = qname.getLocalName(); Pair namespaceEntity = getNamespace(uri); if (namespaceEntity == null) { // There is no match on NS, so there is no QName like this return null; } Long nsId = namespaceEntity.getFirst(); QNameEntity entity = findQNameEntityByNamespaceAndLocalName(nsId, localName); if (entity == null) { return null; } else { return new Pair(entity.getId(), qname); } } public Pair createValue(QName qname) { String uri = qname.getNamespaceURI(); String localName = qname.getLocalName(); // Create namespace Pair namespaceEntity = getOrCreateNamespace(uri); Long nsId = namespaceEntity.getFirst(); // Create QName QNameEntity entity = createQNameEntity(nsId, localName); return new Pair(entity.getId(), qname); } @Override public int updateValue(Long id, QName qname) { String uri = qname.getNamespaceURI(); String localName = qname.getLocalName(); QNameEntity entity = findQNameEntityById(id); if (entity == null) { // No chance of updating return 0; } // Create namespace Pair namespaceEntity = getOrCreateNamespace(uri); Long nsId = namespaceEntity.getFirst(); // Create QName return updateQNameEntity(entity, nsId, localName); } } protected abstract QNameEntity findQNameEntityById(Long id); protected abstract QNameEntity findQNameEntityByNamespaceAndLocalName(Long nsId, String localName); protected abstract QNameEntity createQNameEntity(Long nsId, String localName); protected abstract int updateQNameEntity(QNameEntity entity, Long nsId, String localName); //================================ // Utility method implementations //================================ public Set convertIdsToQNames(Set ids) { Set qnames = new HashSet(ids.size() * 2 + 1); for (Long id : ids) { QName qname = getQName(id).getSecond(); // getQName(id) is never null qnames.add(qname); } return qnames; } public Map convertIdMapToQNameMap(Map idMap) { Map qnameMap = new HashMap(idMap.size() + 3); for (Map.Entry entry : idMap.entrySet()) { QName qname = getQName(entry.getKey()).getSecond(); // getQName(id) is never null qnameMap.put(qname, entry.getValue()); } return qnameMap; } /** * @return Returns a set of IDs mapping to the QNames provided. If create is false * then there will not be corresponding entries for the QNames that don't exist. * So there is no guarantee that the returned set will be ordered the same or even * contain the same number of elements as the original unless create is true. */ public Set convertQNamesToIds(Set qnames, boolean create) { Set qnameIds = new HashSet(qnames.size(), 1.0F); for (QName qname : qnames) { Long qnameEntityId = null; if (create) { qnameEntityId = getOrCreateQName(qname).getFirst(); // getOrCreateQName(qname) is never null } else { Pair qnamePair = getQName(qname); if (qnamePair == null) { // No such qname and we are not creating one continue; } else { qnameEntityId = qnamePair.getFirst(); } } if (qnameEntityId != null) { qnameIds.add(qnameEntityId); } } // Done return qnameIds; } }