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
+ {
+ 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
+ {
+ 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;
+ }
+}
diff --git a/source/java/org/alfresco/repo/domain/hibernate/NamespaceEntityImpl.java b/source/java/org/alfresco/repo/domain/qname/NamespaceEntity.java
similarity index 50%
rename from source/java/org/alfresco/repo/domain/hibernate/NamespaceEntityImpl.java
rename to source/java/org/alfresco/repo/domain/qname/NamespaceEntity.java
index 0bb463296a..ba3450541b 100644
--- a/source/java/org/alfresco/repo/domain/hibernate/NamespaceEntityImpl.java
+++ b/source/java/org/alfresco/repo/domain/qname/NamespaceEntity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005-2007 Alfresco Software Limited.
+ * 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
@@ -22,79 +22,57 @@
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
-package org.alfresco.repo.domain.hibernate;
+package org.alfresco.repo.domain.qname;
-import java.io.Serializable;
-
-import org.alfresco.repo.domain.NamespaceEntity;
-import org.alfresco.repo.domain.QNameEntity;
+import org.alfresco.util.EqualsHelper;
/**
- * Hibernate-specific implementation of the domain entity QnameEntity.
+ * Entity for alf_namespace persistence.
*
* @author Derek Hulley
+ * @since 3.3
*/
-public class NamespaceEntityImpl implements NamespaceEntity, Serializable
+public class NamespaceEntity
{
- private static final long serialVersionUID = -6781559184013949845L;
-
public static final String EMPTY_URI_SUBSTITUTE = ".empty";
+ public static final Long CONST_LONG_ZERO = new Long(0L);
private Long id;
private Long version;
- private String safeUri;
-
- public NamespaceEntityImpl()
+ private String uri;
+
+ public NamespaceEntity()
{
}
- /**
- * @see #getStoreRef()()
- */
+ @Override
public String toString()
{
- return getUri();
+ StringBuilder sb = new StringBuilder(512);
+ sb.append("NamespaceEntity")
+ .append("[ id=").append(id)
+ .append(", uri=").append(uri)
+ .append("]");
+ return sb.toString();
}
-
- /**
- * @see #getKey()
- */
- public boolean equals(Object obj)
+
+ public void incrementVersion()
{
- if (obj == null)
+ if (version >= Short.MAX_VALUE)
{
- return false;
+ this.version = 0L;
}
- else if (obj == this)
+ else
{
- return true;
+ this.version++;
}
- else if (!(obj instanceof QNameEntity))
- {
- return false;
- }
- NamespaceEntity that = (NamespaceEntity) obj;
- return (this.getUri().equals(that.getUri()));
- }
-
- /**
- * @see #getKey()
- */
- public int hashCode()
- {
- return safeUri.hashCode();
}
public Long getId()
{
return id;
}
-
- /**
- * For Hibernate use.
- */
- @SuppressWarnings("unused")
- private void setId(Long id)
+ public void setId(Long id)
{
this.id = id;
}
@@ -103,34 +81,39 @@ public class NamespaceEntityImpl implements NamespaceEntity, Serializable
{
return version;
}
-
- /** For Hibernate use */
- @SuppressWarnings("unused")
- private void setVersion(Long version)
+ public void setVersion(Long version)
{
this.version = version;
}
-
- /** For Hibernate use */
- @SuppressWarnings("unused")
- private String getSafeUri()
- {
- return safeUri;
- }
-
- /** For Hibernate use */
- private void setSafeUri(String safeUri)
- {
- this.safeUri = safeUri;
- }
-
+
public String getUri()
{
- return safeUri.equals(EMPTY_URI_SUBSTITUTE) ? "" : safeUri;
+ return uri;
}
-
public void setUri(String uri)
{
- setSafeUri(uri.length() == 0 ? EMPTY_URI_SUBSTITUTE : uri);
+ this.uri = uri;
}
-}
\ No newline at end of file
+
+ /**
+ * Convenience getter to interpret the {@link #EMPTY_URI_SUBSTITUTE}
+ */
+ public String getUriSafe()
+ {
+ if (EqualsHelper.nullSafeEquals(uri, NamespaceEntity.EMPTY_URI_SUBSTITUTE))
+ {
+ return "";
+ }
+ else
+ {
+ return uri;
+ }
+ }
+ /**
+ * Convenience setter to interpret the {@link #EMPTY_URI_SUBSTITUTE}
+ */
+ public void setUriSafe(String uri)
+ {
+ this.uri = (uri.length() == 0) ? NamespaceEntity.EMPTY_URI_SUBSTITUTE : uri;
+ }
+}
diff --git a/source/java/org/alfresco/repo/domain/QNameDAO.java b/source/java/org/alfresco/repo/domain/qname/QNameDAO.java
similarity index 71%
rename from source/java/org/alfresco/repo/domain/QNameDAO.java
rename to source/java/org/alfresco/repo/domain/qname/QNameDAO.java
index 66c5d9d0d7..49c91e7fec 100644
--- a/source/java/org/alfresco/repo/domain/QNameDAO.java
+++ b/source/java/org/alfresco/repo/domain/qname/QNameDAO.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005-2007 Alfresco Software Limited.
+ * 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
@@ -22,7 +22,7 @@
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
-package org.alfresco.repo.domain;
+package org.alfresco.repo.domain.qname;
import java.util.Map;
import java.util.Set;
@@ -35,7 +35,7 @@ import org.springframework.extensions.surf.util.Pair;
* Data abstraction layer for QName and Namespace entities.
*
* @author Derek Hulley
- * @since 2.1
+ * @since 3.3
*/
public interface QNameDAO
{
@@ -62,12 +62,6 @@ public interface QNameDAO
*/
Pair getOrCreateNamespace(String namespaceUri);
- /**
- * @param namespaceUri the namespace URI to create
- * @return the new namespace pair (id, uri)
- */
- Pair newNamespace(String namespaceUri);
-
/**
* Modifies an existing namespace URI. If the new URI already exists, then no
* new entity is created.
@@ -102,14 +96,39 @@ public interface QNameDAO
Pair getOrCreateQName(QName qname);
/**
- * @param qname the QName to create
- * @return the new QName pair (id, qname)
+ * Modify an existing QName. The ID of the new QName will be the same as the old one
+ * i.e. the old QName will cease to exist and will become the new QName. This allows
+ * QName modification without affecting tables that reference the old QName.
+ *
+ * @param qnameOld the old QName, which must exist
+ * @param qnameNew the new QName, which must not exist
+ * @return the QName pair (id, qname) with the ID unchanged from old to new
*/
- Pair newQName(QName qname);
+ Pair updateQName(QName qnameOld, QName qnameNew);
+ /**
+ * Bulk-convert QName IDs into QNames
+ *
+ * @param ids the IDs
+ * @return the QNames for the IDs given, in the same order
+ */
Set convertIdsToQNames(Set ids);
+ /**
+ * Convenience method to convert map keys from QName IDs to QNames
+ *
+ * @param idMap a map of objects keyed by QName ID
+ * @return a map of the same objects keyed by the equivalent QNames
+ */
Map convertIdMapToQNameMap(Map idMap);
+ /**
+ * Bulk-convert QNames into QName IDs. This is primarily used for generating
+ * SQL IN clause lists for other DAO queries.
+ *
+ * @param qnames the QNames to convert
+ * @param create true to create any missing QName entities
+ * @return returns the QName IDs (order not guaranteed)
+ */
Set convertQNamesToIds(Set qnames, boolean create);
}
diff --git a/source/java/org/alfresco/repo/domain/qname/QNameDAOTest.java b/source/java/org/alfresco/repo/domain/qname/QNameDAOTest.java
new file mode 100644
index 0000000000..ea4bf37696
--- /dev/null
+++ b/source/java/org/alfresco/repo/domain/qname/QNameDAOTest.java
@@ -0,0 +1,224 @@
+/*
+ * 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.qname;
+
+import junit.framework.TestCase;
+
+import org.alfresco.repo.transaction.RetryingTransactionHelper;
+import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
+import org.alfresco.service.ServiceRegistry;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.service.transaction.TransactionService;
+import org.alfresco.util.ApplicationContextHelper;
+import org.alfresco.util.GUID;
+import org.springframework.extensions.surf.util.Pair;
+import org.springframework.context.ApplicationContext;
+
+/**
+ * @see QNameDAO
+ *
+ * @author Derek Hulley
+ * @since 3.3
+ */
+public class QNameDAOTest extends TestCase
+{
+ private ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
+
+ private TransactionService transactionService;
+ private RetryingTransactionHelper txnHelper;
+ private QNameDAO qnameDAO;
+
+ @Override
+ public void setUp() throws Exception
+ {
+ ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY);
+ transactionService = serviceRegistry.getTransactionService();
+ txnHelper = transactionService.getRetryingTransactionHelper();
+
+ qnameDAO = (QNameDAO) ctx.getBean("qnameDAO");
+ }
+
+ private Pair getNamespace(final String uri, final boolean autoCreate, boolean expectSuccess)
+ {
+ RetryingTransactionCallback> callback = new RetryingTransactionCallback>()
+ {
+ public Pair execute() throws Throwable
+ {
+ Pair namespacePair = null;
+ if (autoCreate)
+ {
+ namespacePair = qnameDAO.getOrCreateNamespace(uri);
+ }
+ else
+ {
+ namespacePair = qnameDAO.getNamespace(uri);
+ }
+ return namespacePair;
+ }
+ };
+ try
+ {
+ return txnHelper.doInTransaction(callback, !autoCreate, false);
+ }
+ catch (Throwable e)
+ {
+ if (expectSuccess)
+ {
+ // oops
+ throw new RuntimeException("Expected to get namespace '" + uri + "'.", e);
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+
+ private Pair getQName(final QName qname, final boolean autoCreate, boolean expectSuccess)
+ {
+ RetryingTransactionCallback> callback = new RetryingTransactionCallback>()
+ {
+ public Pair execute() throws Throwable
+ {
+ Pair qnamePair = null;
+ if (autoCreate)
+ {
+ qnamePair = qnameDAO.getOrCreateQName(qname);
+ }
+ else
+ {
+ qnamePair = qnameDAO.getQName(qname);
+ }
+ return qnamePair;
+ }
+ };
+ try
+ {
+ return txnHelper.doInTransaction(callback, !autoCreate, false);
+ }
+ catch (Throwable e)
+ {
+ if (expectSuccess)
+ {
+ // oops
+ throw new RuntimeException("Expected to get qname '" + qname + "'.", e);
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+
+ public void testCreateNamespace() throws Exception
+ {
+ // Create a namespace
+ String uri = GUID.generate();
+ Pair namespacePair = getNamespace(uri, true, true);
+ // Check that it can be retrieved
+ Pair namespacePairCheck = getNamespace(namespacePair.getSecond(), false, true);
+ assertEquals("Namespace ID changed", namespacePair.getFirst(), namespacePairCheck.getFirst());
+ // Check the duplicate checking
+ getNamespace(uri, true, false);
+ }
+
+ public void testCreateNamespaceEmpty() throws Exception
+ {
+ // Create a namespace
+ String uri = "";
+ Pair namespacePair = getNamespace(uri, true, true);
+ // Check that it can be retrieved
+ Pair namespacePairCheck = getNamespace(namespacePair.getSecond(), false, true);
+ assertEquals("Namespace ID changed", namespacePair.getFirst(), namespacePairCheck.getFirst());
+ }
+
+ public void testUpdateNamespace() throws Exception
+ {
+ // Create a namespace
+ final String uri = GUID.generate();
+ Pair namespacePair = getNamespace(uri, true, true);
+ // Use namespace in a QName
+ QName qnameOld = QName.createQName(uri, GUID.generate());
+ Pair qnameOldPair = getQName(qnameOld, true, true);
+ // Now update it
+ final String uri2 = GUID.generate();
+ RetryingTransactionCallback callback = new RetryingTransactionCallback()
+ {
+ public Void execute() throws Throwable
+ {
+ qnameDAO.updateNamespace(uri, uri2);
+ return null;
+ }
+ };
+ transactionService.getRetryingTransactionHelper().doInTransaction(callback);
+
+ // Make sure that the old QName is gone (checks caching)
+ getQName(qnameOld, false, false);
+
+ // The new QName should be there
+ QName qnameNew = QName.createQName(uri2, qnameOld.getLocalName());
+ getQName(qnameNew, false, true);
+
+ // Should be able to create the original namespace again
+ Pair namespacePairAgain = getNamespace(uri, true, true);
+ assertNotSame("Should have a new namespace ID", namespacePair.getFirst(), namespacePairAgain.getFirst());
+ }
+
+ public void testCreateQName() throws Exception
+ {
+ // Create a qname
+ QName qname = QName.createQName(getName(), GUID.generate());
+ Pair qnamePair = getQName(qname, true, true);
+ // Check that it can be retrieved
+ Pair qnamePairCheck = getQName(qnamePair.getSecond(), false, true);
+ assertEquals("QName ID changed", qnamePair.getFirst(), qnamePairCheck.getFirst());
+ // Check the duplicate checking
+ getQName(qname, true, false);
+ }
+
+ public void testUpdateQName() throws Exception
+ {
+ // Create a qname
+ final QName qnameOld = QName.createQName(GUID.generate(), GUID.generate());
+ Pair qnamePairOld = getQName(qnameOld, true, true);
+ // Now update it
+ final QName qnameNew = QName.createQName(GUID.generate(), GUID.generate());
+ RetryingTransactionCallback> callback = new RetryingTransactionCallback>()
+ {
+ public Pair execute() throws Throwable
+ {
+ return qnameDAO.updateQName(qnameOld, qnameNew);
+ }
+ };
+ Pair qnamePairNew = transactionService.getRetryingTransactionHelper().doInTransaction(callback);
+ // The ID must be the same
+ assertEquals("New QName is incorrect", qnameNew, qnamePairNew.getSecond());
+ assertEquals("ID did not remain the same", qnamePairOld.getFirst(), qnamePairNew.getFirst());
+ // The old QName should not be there
+ getQName(qnameOld, false, false);
+ // The new QName should be there
+ getQName(qnameNew, false, true);
+ }
+}
diff --git a/source/java/org/alfresco/repo/domain/qname/QNameEntity.java b/source/java/org/alfresco/repo/domain/qname/QNameEntity.java
new file mode 100644
index 0000000000..8771ac4a85
--- /dev/null
+++ b/source/java/org/alfresco/repo/domain/qname/QNameEntity.java
@@ -0,0 +1,130 @@
+/*
+ * 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.qname;
+
+import org.alfresco.util.EqualsHelper;
+
+/**
+ * Entity for alf_qname persistence.
+ *
+ * @author Derek Hulley
+ * @since 3.3
+ */
+public class QNameEntity
+{
+ public static final String EMPTY_LOCALNAME_SUBSTITUTE = ".empty";
+ public static final Long CONST_LONG_ZERO = new Long(0L);
+
+ private Long id;
+ private Long version;
+ private Long namespaceId;
+ private String localName;
+
+ public QNameEntity()
+ {
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder(512);
+ sb.append("QNameEntity")
+ .append("[ id=").append(id)
+ .append(", nsId=").append(namespaceId)
+ .append(", localName=").append(localName)
+ .append("]");
+ return sb.toString();
+ }
+
+ public void incrementVersion()
+ {
+ if (version >= Short.MAX_VALUE)
+ {
+ this.version = 0L;
+ }
+ else
+ {
+ this.version++;
+ }
+ }
+
+ public Long getId()
+ {
+ return id;
+ }
+ public void setId(Long id)
+ {
+ this.id = id;
+ }
+
+ public Long getVersion()
+ {
+ return version;
+ }
+ public void setVersion(Long version)
+ {
+ this.version = version;
+ }
+
+ public Long getNamespaceId()
+ {
+ return namespaceId;
+ }
+ public void setNamespaceId(Long namespaceId)
+ {
+ this.namespaceId = namespaceId;
+ }
+
+ public String getLocalName()
+ {
+ return localName;
+ }
+ public void setLocalName(String localName)
+ {
+ this.localName = localName;
+ }
+
+ /**
+ * Convenience getter to interpret the {@link #EMPTY_LOCALNAME_SUBSTITUTE}
+ */
+ public String getLocalNameSafe()
+ {
+ if (EqualsHelper.nullSafeEquals(localName, QNameEntity.EMPTY_LOCALNAME_SUBSTITUTE))
+ {
+ return "";
+ }
+ else
+ {
+ return localName;
+ }
+ }
+ /**
+ * Convenience setter to interpret the {@link #EMPTY_LOCALNAME_SUBSTITUTE}
+ */
+ public void setLocalNameSafe(String localName)
+ {
+ this.localName = (localName.length() == 0) ? QNameEntity.EMPTY_LOCALNAME_SUBSTITUTE : localName;
+ }
+}
diff --git a/source/java/org/alfresco/repo/domain/qname/ibatis/QNameDAOImpl.java b/source/java/org/alfresco/repo/domain/qname/ibatis/QNameDAOImpl.java
new file mode 100644
index 0000000000..0e23bc8b48
--- /dev/null
+++ b/source/java/org/alfresco/repo/domain/qname/ibatis/QNameDAOImpl.java
@@ -0,0 +1,130 @@
+/*
+ * 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.qname.ibatis;
+
+import org.alfresco.repo.domain.qname.AbstractQNameDAOImpl;
+import org.alfresco.repo.domain.qname.NamespaceEntity;
+import org.alfresco.repo.domain.qname.QNameEntity;
+import org.springframework.orm.ibatis.SqlMapClientTemplate;
+
+/**
+ * iBatis-specific extension of the QName and Namespace abstract DAO
+ *
+ * @author Derek Hulley
+ * @since 3.3
+ */
+public class QNameDAOImpl extends AbstractQNameDAOImpl
+{
+ private static final String SELECT_NS_BY_ID = "alfresco.qname.select_NamespaceById";
+ private static final String SELECT_NS_BY_URI = "alfresco.qname.select_NamespaceByUri";
+ private static final String INSERT_NS = "alfresco.qname.insert_Namespace";
+ private static final String UPDATE_NS = "alfresco.qname.update_Namespace";
+ private static final String SELECT_QNAME_BY_ID = "alfresco.qname.select_QNameById";
+ private static final String SELECT_QNAME_BY_NS_AND_LOCALNAME = "alfresco.qname.select_QNameByNsAndLocalName";
+ private static final String INSERT_QNAME = "alfresco.qname.insert_QName";
+ private static final String UPDATE_QNAME = "alfresco.qname.update_QName";
+
+ private SqlMapClientTemplate template;
+
+ public void setSqlMapClientTemplate(SqlMapClientTemplate sqlMapClientTemplate)
+ {
+ this.template = sqlMapClientTemplate;
+ }
+
+ @Override
+ protected NamespaceEntity findNamespaceEntityById(Long id)
+ {
+ NamespaceEntity entity = new NamespaceEntity();
+ entity.setId(id);
+ entity = (NamespaceEntity) template.queryForObject(SELECT_NS_BY_ID, entity);
+ return entity;
+ }
+
+ @Override
+ protected NamespaceEntity findNamespaceEntityByUri(String uri)
+ {
+ NamespaceEntity entity = new NamespaceEntity();
+ entity.setUriSafe(uri);
+ entity = (NamespaceEntity) template.queryForObject(SELECT_NS_BY_URI, entity);
+ return entity;
+ }
+
+ @Override
+ protected NamespaceEntity createNamespaceEntity(String uri)
+ {
+ NamespaceEntity entity = new NamespaceEntity();
+ entity.setVersion(NamespaceEntity.CONST_LONG_ZERO);
+ entity.setUriSafe(uri);
+ template.insert(INSERT_NS, entity);
+ return entity;
+ }
+
+ @Override
+ protected int updateNamespaceEntity(NamespaceEntity entity, String uri)
+ {
+ entity.setUriSafe(uri);
+ entity.incrementVersion();
+ return template.update(UPDATE_NS, entity);
+ }
+
+ @Override
+ protected QNameEntity findQNameEntityById(Long id)
+ {
+ QNameEntity entity = new QNameEntity();
+ entity.setId(id);
+ entity = (QNameEntity) template.queryForObject(SELECT_QNAME_BY_ID, entity);
+ return entity;
+ }
+
+ @Override
+ protected QNameEntity findQNameEntityByNamespaceAndLocalName(Long nsId, String localName)
+ {
+ QNameEntity entity = new QNameEntity();
+ entity.setNamespaceId(nsId);
+ entity.setLocalNameSafe(localName);
+ entity = (QNameEntity) template.queryForObject(SELECT_QNAME_BY_NS_AND_LOCALNAME, entity);
+ return entity;
+ }
+
+ @Override
+ protected QNameEntity createQNameEntity(Long nsId, String localName)
+ {
+ QNameEntity entity = new QNameEntity();
+ entity.setVersion(QNameEntity.CONST_LONG_ZERO);
+ entity.setNamespaceId(nsId);
+ entity.setLocalNameSafe(localName);
+ template.insert(INSERT_QNAME, entity);
+ return entity;
+ }
+
+ @Override
+ protected int updateQNameEntity(QNameEntity entity, Long nsId, String localName)
+ {
+ entity.setNamespaceId(nsId);
+ entity.setLocalNameSafe(localName);
+ entity.incrementVersion();
+ return template.update(UPDATE_QNAME, entity);
+ }
+}
diff --git a/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java b/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java
index 8fff95bdc2..c62ccdfc96 100644
--- a/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java
+++ b/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java
@@ -59,7 +59,7 @@ import org.alfresco.repo.domain.NodeAssoc;
import org.alfresco.repo.domain.NodePropertyValue;
import org.alfresco.repo.domain.PropertyMapKey;
import org.alfresco.repo.domain.PropertyValue;
-import org.alfresco.repo.domain.QNameDAO;
+import org.alfresco.repo.domain.qname.QNameDAO;
import org.alfresco.repo.domain.Server;
import org.alfresco.repo.domain.Store;
import org.alfresco.repo.domain.Transaction;
diff --git a/source/java/org/alfresco/service/namespace/QName.java b/source/java/org/alfresco/service/namespace/QName.java
index ffe91d28ea..6ea494fd46 100644
--- a/source/java/org/alfresco/service/namespace/QName.java
+++ b/source/java/org/alfresco/service/namespace/QName.java
@@ -27,7 +27,7 @@ package org.alfresco.service.namespace;
import java.io.Serializable;
import java.util.Collection;
-import org.alfresco.repo.domain.hibernate.NamespaceEntityImpl;
+import org.alfresco.repo.domain.qname.NamespaceEntity;
/**
* QName
represents the qualified name of a Repository item. Each
@@ -231,7 +231,7 @@ public final class QName implements QNamePattern, Serializable, Cloneable, Compa
*/
private QName(String namespace, String name, String prefix)
{
- this.namespaceURI = ((namespace == null) || (namespace.equals(NamespaceEntityImpl.EMPTY_URI_SUBSTITUTE))) ? NamespaceService.DEFAULT_URI : namespace;
+ this.namespaceURI = ((namespace == null) || (namespace.equals(NamespaceEntity.EMPTY_URI_SUBSTITUTE))) ? NamespaceService.DEFAULT_URI : namespace;
this.prefix = prefix;
this.localName = name;
this.hashCode = 0;