* The keys must have good equals and hashCode implementations and
* must respect the case-sensitivity of the use-case.
+ *
+ * All keys will be unique to the given cache region, allowing the cache to be shared
+ * between instances of this class.
*
* @author Derek Hulley
* @since 3.3
*/
public class EntityLookupCache
{
- private static final String NULL_VALUE = "@@NULL_VALUE@@";
-
- private final SimpleCache cache;
- private final EntityLookup entityLookup;
-
- @SuppressWarnings("unchecked")
- public EntityLookupCache(SimpleCache cache, EntityLookup entityLookup)
- {
- this.cache = cache;
- this.entityLookup = entityLookup;
- }
-
/**
* Interface to support lookups of the entities using keys and values.
*/
- public static interface EntityLookup
+ public static interface EntityLookupCallbackDAO
{
/**
* Resolve the given value into a unique value key that can be used to find the entity's ID.
@@ -92,11 +83,49 @@ public class EntityLookupCache createValue(V1 value);
}
+ private static final String NULL_VALUE = "@@NULL_VALUE@@";
+ private static final String CACHE_REGION_DEFAULT = "DEFAULT";
+
+ private final SimpleCache cache;
+ private final EntityLookupCallbackDAO entityLookup;
+ private final String cacheRegion;
+
+ /**
+ * Construct the lookup cache, using the {@link #CACHE_REGION_DEFAULT default cache region}.
+ *
+ * @param cache the cache that will back the two-way lookups
+ * @param entityLookup the instance that is able to find and persist entities
+ */
@SuppressWarnings("unchecked")
- Pair getByKey(K key)
+ public EntityLookupCache(SimpleCache cache, EntityLookupCallbackDAO entityLookup)
{
+ this(cache, CACHE_REGION_DEFAULT, entityLookup);
+ }
+
+ /**
+ * Construct the lookup cache, using the given cache region.
+ *
+ * All keys will be unique to the given cache region, allowing the cache to be shared
+ * between instances of this class.
+ *
+ * @param cache the cache that will back the two-way lookups
+ * @param cacheRegion the region within the cache to use.
+ * @param entityLookup the instance that is able to find and persist entities
+ */
+ @SuppressWarnings("unchecked")
+ public EntityLookupCache(SimpleCache cache, String cacheRegion, EntityLookupCallbackDAO entityLookup)
+ {
+ this.cache = cache;
+ this.entityLookup = entityLookup;
+ this.cacheRegion = cacheRegion;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Pair getByKey(K key)
+ {
+ CacheRegionKey cacheKey = new CacheRegionKey(cacheRegion, key);
// Look in the cache
- V value = (V) cache.get(key);
+ V value = (V) cache.get(cacheKey);
if (value != null && value.equals(NULL_VALUE))
{
// We checked before
@@ -111,24 +140,25 @@ public class EntityLookupCache getByValue(V value)
+ public Pair getByValue(V value)
{
// Get the value key
VK valueKey = entityLookup.getValueKey(value);
+ CacheRegionKey cacheKey = new CacheRegionKey(cacheRegion, valueKey);
// Look in the cache
- K key = (K) cache.get(valueKey);
+ K key = (K) cache.get(cacheKey);
// Check if we have looked this up already
if (key != null && key.equals(NULL_VALUE))
{
@@ -144,24 +174,26 @@ public class EntityLookupCache getOrCreateByValue(V value)
+ public Pair getOrCreateByValue(V value)
{
// Get the value key
VK valueKey = entityLookup.getValueKey(value);
+ CacheRegionKey cacheKey = new CacheRegionKey(cacheRegion, valueKey);
// Look in the cache
- K key = (K) cache.get(valueKey);
+ K key = (K) cache.get(cacheKey);
// Check if the value is already mapped to a key
if (key != null && !key.equals(NULL_VALUE))
{
@@ -176,9 +208,67 @@ public class EntityLookupCache
+public class EntityLookupCacheTest extends TestCase implements EntityLookupCallbackDAO
{
- private EntityLookupCache entityLookupCache;
+ SimpleCache cache;
+ private EntityLookupCache entityLookupCacheA;
+ private EntityLookupCache entityLookupCacheB;
private TreeMap database;
@Override
protected void setUp() throws Exception
{
- SimpleCache cache = new MemoryCache();
- entityLookupCache = new EntityLookupCache(cache, this);
+ cache = new MemoryCache();
+ entityLookupCacheA = new EntityLookupCache(cache, "A", this);
+ entityLookupCacheB = new EntityLookupCache(cache, "B", this);
database = new TreeMap();
}
@@ -63,7 +66,7 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookup entityPair = entityLookupCache.getByValue(value);
+ Pair entityPair = entityLookupCacheA.getByValue(value);
assertNull(entityPair);
assertTrue(database.isEmpty());
// Now do lookup or create
- entityPair = entityLookupCache.getOrCreateByValue(value);
+ entityPair = entityLookupCacheA.getOrCreateByValue(value);
assertNotNull("Expected a value to be found", entityPair);
Long entityId = entityPair.getFirst();
assertTrue("Database ID should have been created", database.containsKey(entityId));
assertEquals("Database value incorrect", value.val, database.get(entityId));
// Do lookup or create again
- entityPair = entityLookupCache.getOrCreateByValue(value);
+ entityPair = entityLookupCacheA.getOrCreateByValue(value);
assertNotNull("Expected a value to be found", entityPair);
assertEquals("Expected same entity ID", entityId, entityPair.getFirst());
// Look it up using the value
- entityPair = entityLookupCache.getByValue(value);
+ entityPair = entityLookupCacheA.getByValue(value);
assertNotNull("Lookup after create should work", entityPair);
// Look it up using the ID
- entityPair = entityLookupCache.getByKey(entityId);
+ entityPair = entityLookupCacheA.getByKey(entityId);
assertNotNull("Lookup by key should work after create", entityPair);
assertTrue("Looked-up type incorrect", entityPair.getSecond() instanceof TestValue);
assertEquals("Looked-up type value incorrect", value, entityPair.getSecond());
@@ -109,20 +112,41 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookup entityPair = entityLookupCache.getByValue(new TestValue("AAA"));
+ Pair entityPair = entityLookupCacheA.getByValue(new TestValue("AAA"));
assertNotNull("Expected value to be found", entityPair);
assertEquals("ID is incorrect", new Long(1), entityPair.getFirst());
// Look up by ID
- entityPair = entityLookupCache.getByKey(new Long(2));
+ entityPair = entityLookupCacheA.getByKey(new Long(2));
assertNotNull("Expected value to be found", entityPair);
// Do lookup or create
- entityPair = entityLookupCache.getByValue(new TestValue("CCC"));
+ entityPair = entityLookupCacheA.getByValue(new TestValue("CCC"));
assertNotNull("Expected value to be found", entityPair);
assertEquals("ID is incorrect", new Long(3), entityPair.getFirst());
}
+ public void testRegions() throws Exception
+ {
+ TestValue valueAAA = new TestValue("AAA");
+ Pair entityPairAAA = entityLookupCacheA.getOrCreateByValue(valueAAA);
+ assertNotNull(entityPairAAA);
+ assertEquals("AAA", database.get(entityPairAAA.getFirst()));
+ assertEquals(2, cache.getKeys().size());
+
+ TestValue valueBBB = new TestValue("BBB");
+ Pair entityPairBBB = entityLookupCacheB.getOrCreateByValue(valueBBB);
+ assertNotNull(entityPairBBB);
+ assertEquals("BBB", database.get(entityPairBBB.getFirst()));
+ assertEquals(4, cache.getKeys().size());
+
+ // Now cross-check against the caches and make sure that the cache
+ entityPairBBB = entityLookupCacheA.getByValue(valueBBB);
+ assertEquals(5, cache.getKeys().size());
+ entityPairBBB = entityLookupCacheB.getByValue(valueAAA);
+ assertEquals(6, cache.getKeys().size());
+ }
+
/**
* Helper class to represent business object
*/
diff --git a/source/java/org/alfresco/repo/domain/CrcHelper.java b/source/java/org/alfresco/repo/domain/CrcHelper.java
new file mode 100644
index 0000000000..0404202747
--- /dev/null
+++ b/source/java/org/alfresco/repo/domain/CrcHelper.java
@@ -0,0 +1,119 @@
+/*
+ * 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;
+
+import java.io.UnsupportedEncodingException;
+import java.util.zip.CRC32;
+
+import org.alfresco.util.Pair;
+
+/**
+ * Helper class to calculate CRC values for string persistence.
+ *
+ * @author Derek Hulley
+ * @since 3.3
+ */
+public class CrcHelper
+{
+ public static final String EMPTY_STRING = ".empty";
+
+ /**
+ * Calculate a persistable, unique pair of values that can be persisted in a database unique
+ * key and guarantee correct case-sensitivity.
+ *
+ * While the short-string version of the value is always lowercase, the CRC is
+ * calculated from the virgin string if case-sensitivity is enforced; in the case-insensitive
+ * case, the CRC is calculated from a lowercase version of the string.
+ *
+ * If the value is an empty string, then {@link #EMPTY_STRING} is used instead. This ensures
+ * that persisted values don't fall foul of the Oracle empty string comparison "behaviour" i.e
+ * you should never persist an empty string in Oracle as it equates to a SQL NULL.
+ *
+ * @param value the raw value that will be persisted
+ * @param dataLength the maximum number of characters that can be persisted
+ * @param useCharsFromStart true if the shortened string value must be made from
+ * the first characters of the string or false to use
+ * characters from the end of the string.
+ * @param caseSensitive true if the resulting pair must be case-sensitive or
+ * false if the pair must be case-insensitive.
+ * @return Return the persistable pair. The result will never be null,
+ * but the individual pair values will be null if the
+ * value given is null
+ */
+ public static Pair getStringCrcPair(
+ String value,
+ int dataLength,
+ boolean useCharsFromStart,
+ boolean caseSensitive)
+ {
+ String valueLowerCase;
+ if (value == null)
+ {
+ return new Pair(null, null);
+ }
+ else if (value.length() == 0)
+ {
+ value = CrcHelper.EMPTY_STRING;
+ valueLowerCase = value;
+ }
+ else
+ {
+ valueLowerCase = value.toLowerCase();
+ }
+ Long valueCrc;
+ try
+ {
+ CRC32 crc = new CRC32();
+ if (caseSensitive)
+ {
+ crc.update(value.getBytes("UTF-8"));
+ }
+ else
+ {
+ crc.update(valueLowerCase.getBytes("UTF-8"));
+ }
+ valueCrc = crc.getValue();
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ throw new RuntimeException("UTF-8 encoding is not supported");
+ }
+ // Get the short value (case-sensitive or not)
+ String valueShort = null;
+ int valueLen = valueLowerCase.length();
+ if (valueLen < dataLength)
+ {
+ valueShort = value;
+ }
+ else if (useCharsFromStart)
+ {
+ valueShort = valueLowerCase.substring(0, dataLength - 1);
+ }
+ else
+ {
+ valueShort = valueLowerCase.substring(valueLen - dataLength);
+ }
+ return new Pair(valueShort, valueCrc);
+ }
+}
diff --git a/source/java/org/alfresco/repo/domain/DomainTestSuite.java b/source/java/org/alfresco/repo/domain/DomainTestSuite.java
index 7db49bd7ad..314110adce 100644
--- a/source/java/org/alfresco/repo/domain/DomainTestSuite.java
+++ b/source/java/org/alfresco/repo/domain/DomainTestSuite.java
@@ -52,6 +52,7 @@ public class DomainTestSuite extends TestSuite
suite.addTestSuite(LocaleDAOTest.class);
suite.addTestSuite(PropertyValueTest.class);
suite.addTestSuite(QNameDAOTest.class);
+ suite.addTestSuite(PropertyValueTest.class);
return suite;
}
diff --git a/source/java/org/alfresco/repo/domain/contentdata/ContentUrlEntity.java b/source/java/org/alfresco/repo/domain/contentdata/ContentUrlEntity.java
index 3dbcd77429..b4827e8f8c 100644
--- a/source/java/org/alfresco/repo/domain/contentdata/ContentUrlEntity.java
+++ b/source/java/org/alfresco/repo/domain/contentdata/ContentUrlEntity.java
@@ -1,245 +1,198 @@
-/*
- * 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.contentdata;
-
-import java.io.UnsupportedEncodingException;
-import java.util.zip.CRC32;
-
-import org.alfresco.util.EqualsHelper;
-import org.alfresco.util.Pair;
-
-/**
- * Entity bean for alf_content_url table.
- *
- * These are unique (see {@link #equals(Object) equals} and {@link #hashCode() hashCode}) based
- * on the {@link #getContentUrl() content URL} value.
- *
- * @author Derek Hulley
- * @since 3.2
- */
-public class ContentUrlEntity
-{
- public static final Long CONST_LONG_ZERO = new Long(0L);
- public static final String EMPTY_URL = "empty";
-
- private Long id;
- private Long version;
- private String contentUrl;
- private String contentUrlShort;
- private long contentUrlCrc;
- private long size;
-
- public ContentUrlEntity()
- {
- this.size = 0L;
- }
-
- @Override
- public int hashCode()
- {
- return (contentUrl == null ? 0 : contentUrl.hashCode());
- }
-
- @Override
- public boolean equals(Object obj)
- {
- if (this == obj)
- {
- return true;
- }
- else if (obj instanceof ContentUrlEntity)
- {
- ContentUrlEntity that = (ContentUrlEntity) obj;
- return EqualsHelper.nullSafeEquals(this.contentUrl, that.contentUrl);
- }
- else
- {
- return false;
- }
- }
-
- @Override
- public String toString()
- {
- StringBuilder sb = new StringBuilder(512);
- sb.append("ContentUrlEntity")
- .append("[ ID=").append(id)
- .append(", contentUrl=").append(contentUrl)
- .append(", size=").append(size)
- .append("]");
- return sb.toString();
- }
-
- /**
- * @param
- * @return Returns a pair of the short (12 chars lowercase) URL and the CRC value
- */
- private static Pair getContentUrlCrcPair(String internalContentUrl)
- {
- if (internalContentUrl == null)
- {
- return new Pair(null, null);
- }
-
- // Calculate the CRC value
- CRC32 crc = new CRC32();
- try
- {
- crc.update(internalContentUrl.getBytes("UTF-8"));
- }
- catch (UnsupportedEncodingException e)
- {
- throw new RuntimeException("UTF-8 encoding is not supported");
- }
- // Get the short name (case-insensitive)
- String contentUrlShort = null;
- int contentUrlLen = internalContentUrl.length();
- if (contentUrlLen < 12)
- {
- contentUrlShort = internalContentUrl.toLowerCase();
- }
- else
- {
- contentUrlShort = internalContentUrl.toLowerCase().substring(contentUrlLen - 12);
- }
- // Done
- return new Pair(contentUrlShort, crc.getValue());
- }
-
- private static String getInternalUrl(String contentUrl)
- {
- if (contentUrl == null)
- {
- return null;
- }
- // Deal with Oracle's NULL-EMPTY confusion
- if (contentUrl.length() == 0)
- {
- return EMPTY_URL;
- }
- else
- {
- return contentUrl;
- }
- }
-
- /**
- * @return Returns the originally-set content URL
- */
- private static String getExternalUrl(String contentUrl)
- {
- if (contentUrl == null)
- {
- return null;
- }
- // Decode Oracle's NULL-EMPTY confusion
- if (contentUrl.equals(EMPTY_URL))
- {
- return "";
- }
- else
- {
- return contentUrl;
- }
- }
-
- 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 String getContentUrl()
- {
- // Convert the persisted content URL to an external value
- return ContentUrlEntity.getExternalUrl(contentUrl);
- }
-
- public void setContentUrl(String contentUrl)
- {
- this.contentUrl = contentUrl;
- // Convert the URL to a persistable value
- String internalContentUrl = ContentUrlEntity.getInternalUrl(contentUrl);
- Pair contentUrlPair = ContentUrlEntity.getContentUrlCrcPair(internalContentUrl);
- this.contentUrlShort = contentUrlPair.getFirst();
- this.contentUrlCrc = contentUrlPair.getSecond();
- }
-
- /**
- * For persistence use
- */
- public String getContentUrlShort()
- {
- return contentUrlShort;
- }
-
- /**
- * For persistence use
- */
- public void setContentUrlShort(String contentUrlShort)
- {
- this.contentUrlShort = contentUrlShort;
- }
-
- /**
- * For persistence use
- */
- public long getContentUrlCrc()
- {
- return contentUrlCrc;
- }
-
- /**
- * For persistence use
- */
- public void setContentUrlCrc(long contentUrlCrc)
- {
- this.contentUrlCrc = contentUrlCrc;
- }
-
- public long getSize()
- {
- return size;
- }
-
- public void setSize(long size)
- {
- this.size = size;
- }
-}
+/*
+ * 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.contentdata;
+
+import org.alfresco.repo.domain.CrcHelper;
+import org.alfresco.util.EqualsHelper;
+import org.alfresco.util.Pair;
+
+/**
+ * Entity bean for alf_content_url table.
+ *
+ * These are unique (see {@link #equals(Object) equals} and {@link #hashCode() hashCode}) based
+ * on the {@link #getContentUrl() content URL} value.
+ *
+ * @author Derek Hulley
+ * @since 3.2
+ */
+public class ContentUrlEntity
+{
+ public static final Long CONST_LONG_ZERO = new Long(0L);
+ public static final String EMPTY_URL = "empty";
+
+ private Long id;
+ private Long version;
+ private String contentUrl;
+ private String contentUrlShort;
+ private long contentUrlCrc;
+ private long size;
+
+ public ContentUrlEntity()
+ {
+ this.size = 0L;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return (contentUrl == null ? 0 : contentUrl.hashCode());
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ else if (obj instanceof ContentUrlEntity)
+ {
+ ContentUrlEntity that = (ContentUrlEntity) obj;
+ return EqualsHelper.nullSafeEquals(this.contentUrl, that.contentUrl);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder(512);
+ sb.append("ContentUrlEntity")
+ .append("[ ID=").append(id)
+ .append(", contentUrl=").append(contentUrl)
+ .append(", size=").append(size)
+ .append("]");
+ return sb.toString();
+ }
+
+ /**
+ * @param
+ * @return Returns a pair of the short (12 chars lowercase) URL and the CRC value
+ */
+ private static Pair getContentUrlCrcPair(String internalContentUrl)
+ {
+ return CrcHelper.getStringCrcPair(internalContentUrl, 12, false, true);
+ }
+
+ /**
+ * @return Returns the originally-set content URL
+ */
+ private static String getExternalUrl(String contentUrl)
+ {
+ if (contentUrl == null)
+ {
+ return null;
+ }
+ // Decode Oracle's NULL-EMPTY confusion
+ if (contentUrl.equals(EMPTY_URL))
+ {
+ return "";
+ }
+ else
+ {
+ return contentUrl;
+ }
+ }
+
+ 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 String getContentUrl()
+ {
+ // Convert the persisted content URL to an external value
+ return ContentUrlEntity.getExternalUrl(contentUrl);
+ }
+
+ public void setContentUrl(String contentUrl)
+ {
+ this.contentUrl = contentUrl;
+ // Convert the URL to a persistable value
+ Pair contentUrlPair = ContentUrlEntity.getContentUrlCrcPair(contentUrl);
+ this.contentUrlShort = contentUrlPair.getFirst();
+ this.contentUrlCrc = contentUrlPair.getSecond();
+ }
+
+ /**
+ * For persistence use
+ */
+ public String getContentUrlShort()
+ {
+ return contentUrlShort;
+ }
+
+ /**
+ * For persistence use
+ */
+ public void setContentUrlShort(String contentUrlShort)
+ {
+ this.contentUrlShort = contentUrlShort;
+ }
+
+ /**
+ * For persistence use
+ */
+ public long getContentUrlCrc()
+ {
+ return contentUrlCrc;
+ }
+
+ /**
+ * For persistence use
+ */
+ public void setContentUrlCrc(long contentUrlCrc)
+ {
+ this.contentUrlCrc = contentUrlCrc;
+ }
+
+ public long getSize()
+ {
+ return size;
+ }
+
+ public void setSize(long size)
+ {
+ this.size = size;
+ }
+}
diff --git a/source/java/org/alfresco/repo/domain/encoding/AbstractEncodingDAOImpl.java b/source/java/org/alfresco/repo/domain/encoding/AbstractEncodingDAOImpl.java
index 025e89507b..24cdb12474 100644
--- a/source/java/org/alfresco/repo/domain/encoding/AbstractEncodingDAOImpl.java
+++ b/source/java/org/alfresco/repo/domain/encoding/AbstractEncodingDAOImpl.java
@@ -1,141 +1,140 @@
-/*
- * 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.encoding;
-
-import java.io.Serializable;
-
-import org.alfresco.error.AlfrescoRuntimeException;
-import org.alfresco.repo.cache.SimpleCache;
-import org.alfresco.util.Pair;
-import org.alfresco.util.ParameterCheck;
-
-/**
- * Abstract implementation for Encoding DAO.
- *
- * This provides basic services such as caching, but defers to the underlying implementation
- * for CRUD operations.
- *
- * @author Derek Hulley
- * @since 3.2
- */
-public abstract class AbstractEncodingDAOImpl implements EncodingDAO
-{
- private static final Long CACHE_NULL_LONG = Long.MIN_VALUE;
- private static final String NULL_SAFE_STRING = ".null";
- private SimpleCache encodingEntityCache;
-
- /**
- *
- * @param encodingEntityCache the cache of IDs to mimetypes
- */
- public void setEncodingEntityCache(SimpleCache encodingEntityCache)
- {
- this.encodingEntityCache = encodingEntityCache;
- }
-
- public Pair getEncoding(Long id)
- {
- // Check the cache
- String encoding = (String) encodingEntityCache.get(id);
- if (encoding != null)
- {
- return new Pair(id, encoding);
- }
- // Get it from the DB
- EncodingEntity mimetypeEntity = getEncodingEntity(id);
- if (mimetypeEntity == null)
- {
- throw new AlfrescoRuntimeException("The MimetypeEntity ID " + id + " doesn't exist.");
- }
- encoding = mimetypeEntity.getEncoding();
- // Cache it
- encodingEntityCache.put(encoding, id);
- encodingEntityCache.put(id, encoding);
- // Done
- return new Pair(id, encoding);
- }
-
- public Pair getEncoding(String encoding)
- {
- ParameterCheck.mandatory("encoding", encoding);
-
- // Check the cache
- Long id = (Long) encodingEntityCache.get(encoding);
- if (id != null)
- {
- if (id.equals(CACHE_NULL_LONG))
- {
- return null;
- }
- else
- {
- return new Pair(id, encoding);
- }
- }
- // It's not in the cache, so query
- EncodingEntity result = getEncodingEntity(encoding);
- if (result == null)
- {
- // Cache it
- encodingEntityCache.put(encoding, CACHE_NULL_LONG);
- // Done
- return null;
- }
- else
- {
- id = result.getId();
- // Cache it
- encodingEntityCache.put(id, encoding);
- encodingEntityCache.put(encoding, id);
- // Done
- return new Pair(id, encoding);
- }
- }
-
- public Pair getOrCreateEncoding(String encoding)
- {
- ParameterCheck.mandatory("encoding", encoding);
-
- Pair result = getEncoding(encoding);
- if (result == null)
- {
- EncodingEntity encodingEntity = createEncodingEntity(encoding);
- Long id = encodingEntity.getId();
- result = new Pair(id, encoding);
- // Cache it
- encodingEntityCache.put(id, encoding);
- encodingEntityCache.put(encoding, id);
- }
- return result;
- }
-
- /**
- * @param id the ID of the encoding entity
- * @return Return the entity or null if it doesn't exist
- */
- protected abstract EncodingEntity getEncodingEntity(Long id);
- protected abstract EncodingEntity getEncodingEntity(String encoding);
- protected abstract EncodingEntity createEncodingEntity(String encoding);
-}
+/*
+ * 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.encoding;
+
+import java.io.Serializable;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.repo.cache.SimpleCache;
+import org.alfresco.util.Pair;
+import org.alfresco.util.ParameterCheck;
+
+/**
+ * Abstract implementation for Encoding DAO.
+ *
+ * This provides basic services such as caching, but defers to the underlying implementation
+ * for CRUD operations.
+ *
+ * @author Derek Hulley
+ * @since 3.2
+ */
+public abstract class AbstractEncodingDAOImpl implements EncodingDAO
+{
+ private static final Long CACHE_NULL_LONG = Long.MIN_VALUE;
+ private SimpleCache encodingEntityCache;
+
+ /**
+ *
+ * @param encodingEntityCache the cache of IDs to mimetypes
+ */
+ public void setEncodingEntityCache(SimpleCache encodingEntityCache)
+ {
+ this.encodingEntityCache = encodingEntityCache;
+ }
+
+ public Pair getEncoding(Long id)
+ {
+ // Check the cache
+ String encoding = (String) encodingEntityCache.get(id);
+ if (encoding != null)
+ {
+ return new Pair(id, encoding);
+ }
+ // Get it from the DB
+ EncodingEntity mimetypeEntity = getEncodingEntity(id);
+ if (mimetypeEntity == null)
+ {
+ throw new AlfrescoRuntimeException("The MimetypeEntity ID " + id + " doesn't exist.");
+ }
+ encoding = mimetypeEntity.getEncoding();
+ // Cache it
+ encodingEntityCache.put(encoding, id);
+ encodingEntityCache.put(id, encoding);
+ // Done
+ return new Pair(id, encoding);
+ }
+
+ public Pair getEncoding(String encoding)
+ {
+ ParameterCheck.mandatory("encoding", encoding);
+
+ // Check the cache
+ Long id = (Long) encodingEntityCache.get(encoding);
+ if (id != null)
+ {
+ if (id.equals(CACHE_NULL_LONG))
+ {
+ return null;
+ }
+ else
+ {
+ return new Pair(id, encoding);
+ }
+ }
+ // It's not in the cache, so query
+ EncodingEntity result = getEncodingEntity(encoding);
+ if (result == null)
+ {
+ // Cache it
+ encodingEntityCache.put(encoding, CACHE_NULL_LONG);
+ // Done
+ return null;
+ }
+ else
+ {
+ id = result.getId();
+ // Cache it
+ encodingEntityCache.put(id, encoding);
+ encodingEntityCache.put(encoding, id);
+ // Done
+ return new Pair(id, encoding);
+ }
+ }
+
+ public Pair getOrCreateEncoding(String encoding)
+ {
+ ParameterCheck.mandatory("encoding", encoding);
+
+ Pair result = getEncoding(encoding);
+ if (result == null)
+ {
+ EncodingEntity encodingEntity = createEncodingEntity(encoding);
+ Long id = encodingEntity.getId();
+ result = new Pair(id, encoding);
+ // Cache it
+ encodingEntityCache.put(id, encoding);
+ encodingEntityCache.put(encoding, id);
+ }
+ return result;
+ }
+
+ /**
+ * @param id the ID of the encoding entity
+ * @return Return the entity or null if it doesn't exist
+ */
+ protected abstract EncodingEntity getEncodingEntity(Long id);
+ protected abstract EncodingEntity getEncodingEntity(String encoding);
+ protected abstract EncodingEntity createEncodingEntity(String encoding);
+}
diff --git a/source/java/org/alfresco/repo/domain/propval/AbstractPropertyValueDAOImpl.java b/source/java/org/alfresco/repo/domain/propval/AbstractPropertyValueDAOImpl.java
new file mode 100644
index 0000000000..b9178837d3
--- /dev/null
+++ b/source/java/org/alfresco/repo/domain/propval/AbstractPropertyValueDAOImpl.java
@@ -0,0 +1,128 @@
+/*
+ * 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.propval;
+
+import java.io.Serializable;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+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;
+
+/**
+ * Abstract implementation for Property Value DAO.
+ *
+ * This provides basic services such as caching, but defers to the underlying implementation
+ * for CRUD operations.
+ *
+ * @author Derek Hulley
+ * @since 3.3
+ */
+public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO
+{
+ private static final String CACHE_REGION_PROPERTY_CLASS = "PropertyClass";
+ private EntityLookupCache, String> propertyClassCache;
+
+ /**
+ *
+ * @param propertyClassCache the cache of IDs to property classes
+ */
+ public void setPropertyClassCache(SimpleCache propertyClassCache)
+ {
+ PropertyValueCallbackDAO daoCallback = new PropertyValueCallbackDAO();
+ this.propertyClassCache = new EntityLookupCache, String>(
+ propertyClassCache,
+ CACHE_REGION_PROPERTY_CLASS,
+ daoCallback);
+ }
+
+ public Pair> getPropertyClass(Long id)
+ {
+ Pair> entityPair = propertyClassCache.getByKey(id);
+ if (entityPair == null)
+ {
+ throw new AlfrescoRuntimeException("No property class exists for ID " + id);
+ }
+ return entityPair;
+ }
+
+ public Pair> getPropertyClass(Class> clazz)
+ {
+ Pair> entityPair = propertyClassCache.getByValue(clazz);
+ return entityPair;
+ }
+
+ public Pair> getOrCreatePropertyClass(Class> clazz)
+ {
+ Pair> entityPair = propertyClassCache.getOrCreateByValue(clazz);
+ return entityPair;
+ }
+
+ /**
+ * Callback for alf_prop_type DAO.
+ */
+ private class PropertyValueCallbackDAO implements EntityLookupCallbackDAO, String>
+ {
+ private final Pair> convertEntityToPair(PropertyClassEntity propertyClassEntity)
+ {
+ if (propertyClassEntity == null)
+ {
+ return null;
+ }
+ else
+ {
+ return propertyClassEntity.getEntityPair();
+ }
+ }
+
+ public String getValueKey(Class> value)
+ {
+ return value.getName();
+ }
+
+ public Pair> createValue(Class> value)
+ {
+ PropertyClassEntity propertyClassEntity = createClass(value);
+ return convertEntityToPair(propertyClassEntity);
+ }
+
+ public Pair> findByKey(Long key)
+ {
+ PropertyClassEntity propertyClassEntity = findClassById(key);
+ return convertEntityToPair(propertyClassEntity);
+ }
+
+ public Pair> findByValue(Class> value)
+ {
+ PropertyClassEntity propertyClassEntity = findClassByValue(value);
+ return convertEntityToPair(propertyClassEntity);
+ }
+ }
+
+ protected abstract PropertyClassEntity createClass(Class> value);
+ protected abstract PropertyClassEntity findClassById(Long id);
+ protected abstract PropertyClassEntity findClassByValue(Class> value);
+}
diff --git a/source/java/org/alfresco/repo/domain/propval/PropertyClassEntity.java b/source/java/org/alfresco/repo/domain/propval/PropertyClassEntity.java
new file mode 100644
index 0000000000..5329336289
--- /dev/null
+++ b/source/java/org/alfresco/repo/domain/propval/PropertyClassEntity.java
@@ -0,0 +1,172 @@
+/*
+ * 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.propval;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.repo.domain.CrcHelper;
+import org.alfresco.util.EqualsHelper;
+import org.alfresco.util.Pair;
+
+/**
+ * Entity bean for alf_prop_class table.
+ *
+ * @author Derek Hulley
+ * @since 3.3
+ */
+public class PropertyClassEntity
+{
+ public static final Long CONST_LONG_ZERO = new Long(0L);
+ public static final String EMPTY_URL = "empty";
+
+ private Long id;
+ private Long version;
+ private Class> javaClass;
+ private String javaClassName;
+ private String javaClassNameShort;
+ private long javaClassNameCrc;
+
+ public PropertyClassEntity()
+ {
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return (javaClass == null ? 0 : javaClass.hashCode());
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ else if (obj instanceof PropertyClassEntity)
+ {
+ PropertyClassEntity that = (PropertyClassEntity) obj;
+ return EqualsHelper.nullSafeEquals(this.javaClass, that.javaClass);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder(512);
+ sb.append("PropertyClassEntity")
+ .append("[ ID=").append(id)
+ .append(", javaClass=").append(javaClass)
+ .append("]");
+ return sb.toString();
+ }
+
+ /**
+ * @return Returns the ID-class pair
+ */
+ public Pair> getEntityPair()
+ {
+ return new Pair>(id, getJavaClass());
+ }
+
+ public Class> getJavaClass()
+ {
+ if (javaClass == null && javaClassName != null)
+ {
+ try
+ {
+ javaClass = Class.forName(javaClassName);
+ }
+ catch (ClassNotFoundException e)
+ {
+ throw new AlfrescoRuntimeException(
+ "Property class '" + javaClassName + "' is not available to the VM");
+ }
+ }
+ return javaClass;
+ }
+
+ 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 void setJavaClass(Class> javaClass)
+ {
+ this.javaClass = javaClass;
+ this.javaClassName = javaClass.getName();
+ Pair crcPair = CrcHelper.getStringCrcPair(javaClassName, 32, true, true);
+ this.javaClassNameShort = crcPair.getFirst();
+ this.javaClassNameCrc = crcPair.getSecond();
+ }
+
+ public String getJavaClassName()
+ {
+ return javaClassName;
+ }
+
+ public void setJavaClassName(String javaClassName)
+ {
+ this.javaClassName = javaClassName;
+ }
+
+ public String getJavaClassNameShort()
+ {
+ return javaClassNameShort;
+ }
+
+ public void setJavaClassNameShort(String javaClassNameShort)
+ {
+ this.javaClassNameShort = javaClassNameShort;
+ }
+
+ public long getJavaClassNameCrc()
+ {
+ return javaClassNameCrc;
+ }
+
+ public void setJavaClassNameCrc(long javaClassNameCrc)
+ {
+ this.javaClassNameCrc = javaClassNameCrc;
+ }
+}
diff --git a/source/java/org/alfresco/repo/domain/propval/PropertyValueDAO.java b/source/java/org/alfresco/repo/domain/propval/PropertyValueDAO.java
new file mode 100644
index 0000000000..cebb8e18c0
--- /dev/null
+++ b/source/java/org/alfresco/repo/domain/propval/PropertyValueDAO.java
@@ -0,0 +1,42 @@
+/*
+ * 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.propval;
+
+import org.alfresco.util.Pair;
+
+/**
+ * DAO services for alf_prop_XXX tables.
+ *
+ * @author Derek Hulley
+ * @since 3.3
+ */
+public interface PropertyValueDAO
+{
+ Pair> getPropertyClass(Class> clazz);
+
+ Pair> getPropertyClass(Long id);
+
+ Pair> getOrCreatePropertyClass(Class> clazz);
+}
diff --git a/source/java/org/alfresco/repo/domain/propval/PropertyValueDAOTest.java b/source/java/org/alfresco/repo/domain/propval/PropertyValueDAOTest.java
new file mode 100644
index 0000000000..ef17f75d6f
--- /dev/null
+++ b/source/java/org/alfresco/repo/domain/propval/PropertyValueDAOTest.java
@@ -0,0 +1,121 @@
+/*
+ * 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.propval;
+
+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.transaction.TransactionService;
+import org.alfresco.util.ApplicationContextHelper;
+import org.alfresco.util.Pair;
+import org.springframework.context.ApplicationContext;
+
+/**
+ * @see PropertyValueDAO
+ *
+ * @author Derek Hulley
+ * @since 3.3
+ */
+public class PropertyValueDAOTest extends TestCase
+{
+ private ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
+
+ private TransactionService transactionService;
+ private RetryingTransactionHelper txnHelper;
+ private PropertyValueDAO propertyValueDAO;
+
+ @Override
+ public void setUp() throws Exception
+ {
+ ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY);
+ transactionService = serviceRegistry.getTransactionService();
+ txnHelper = transactionService.getRetryingTransactionHelper();
+
+ propertyValueDAO = (PropertyValueDAO) ctx.getBean("propertyValueDAO");
+ }
+
+ public void testPropertyClass() throws Exception
+ {
+ final Class> clazz = this.getClass();
+ RetryingTransactionCallback>> createClassCallback = new RetryingTransactionCallback>>()
+ {
+ public Pair> execute() throws Throwable
+ {
+ // Get the classes
+ return propertyValueDAO.getOrCreatePropertyClass(clazz);
+ }
+ };
+ final Pair> clazzEntityPair = txnHelper.doInTransaction(createClassCallback, false);
+ assertNotNull(clazzEntityPair);
+ assertNotNull(clazzEntityPair.getFirst());
+ assertEquals(clazz, clazzEntityPair.getSecond());
+ // Now retrieve it
+ RetryingTransactionCallback getClassCallback = new RetryingTransactionCallback()
+ {
+ public Void execute() throws Throwable
+ {
+ Pair> checkPair1 = propertyValueDAO.getPropertyClass(clazzEntityPair.getFirst());
+ assertEquals(clazzEntityPair, checkPair1);
+ Pair> checkPair2 = propertyValueDAO.getPropertyClass(clazzEntityPair.getSecond());
+ assertEquals(clazzEntityPair, checkPair2);
+ return null;
+ }
+ };
+ txnHelper.doInTransaction(getClassCallback, true);
+
+ // Test failure when requesting invalid ID
+ RetryingTransactionCallback badGetCallback = new RetryingTransactionCallback()
+ {
+ public Void execute() throws Throwable
+ {
+ propertyValueDAO.getPropertyClass(Long.MIN_VALUE);
+ return null;
+ }
+ };
+ try
+ {
+ txnHelper.doInTransaction(badGetCallback, false);
+ fail("Expected exception when using invalid ID.");
+ }
+ catch (RuntimeException e)
+ {
+ // Expected
+ }
+
+ // Test null caching
+ RetryingTransactionCallback noHitCallback = new RetryingTransactionCallback()
+ {
+ public Void execute() throws Throwable
+ {
+ propertyValueDAO.getPropertyClass(this.getClass());
+ propertyValueDAO.getPropertyClass(this.getClass());
+ return null;
+ }
+ };
+ txnHelper.doInTransaction(noHitCallback, false);
+ }
+}
diff --git a/source/java/org/alfresco/repo/domain/propval/ibatis/PropertyValueDAOImpl.java b/source/java/org/alfresco/repo/domain/propval/ibatis/PropertyValueDAOImpl.java
new file mode 100644
index 0000000000..137be1c5de
--- /dev/null
+++ b/source/java/org/alfresco/repo/domain/propval/ibatis/PropertyValueDAOImpl.java
@@ -0,0 +1,82 @@
+/*
+ * 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.propval.ibatis;
+
+import org.alfresco.repo.domain.propval.AbstractPropertyValueDAOImpl;
+import org.alfresco.repo.domain.propval.PropertyClassEntity;
+import org.springframework.orm.ibatis.SqlMapClientTemplate;
+
+/**
+ * iBatis-specific implementation of the PropertyValue DAO.
+ *
+ * @author Derek Hulley
+ * @since 3.3
+ */
+public class PropertyValueDAOImpl extends AbstractPropertyValueDAOImpl
+{
+ private static final Long VERSION_ONE = new Long(1L);
+ private static final String SELECT_PROPERTY_CLASS_BY_ID = "select.PropertyClassByID";
+ private static final String SELECT_PROPERTY_CLASS_BY_NAME = "select.PropertyClassByName";
+ private static final String INSERT_PROPERTY_CLASS = "insert.PropertyClass";
+
+ private SqlMapClientTemplate template;
+
+ public void setSqlMapClientTemplate(SqlMapClientTemplate sqlMapClientTemplate)
+ {
+ this.template = sqlMapClientTemplate;
+ }
+
+ @Override
+ protected PropertyClassEntity findClassById(Long id)
+ {
+ PropertyClassEntity propertyClassEntity = new PropertyClassEntity();
+ propertyClassEntity.setId(id);
+ propertyClassEntity = (PropertyClassEntity) template.queryForObject(SELECT_PROPERTY_CLASS_BY_ID, propertyClassEntity);
+ // Done
+ return propertyClassEntity;
+ }
+
+ @Override
+ protected PropertyClassEntity findClassByValue(Class> value)
+ {
+ PropertyClassEntity propertyClassEntity = new PropertyClassEntity();
+ propertyClassEntity.setJavaClass(value);
+ propertyClassEntity = (PropertyClassEntity) template.queryForObject(SELECT_PROPERTY_CLASS_BY_NAME, propertyClassEntity);
+ // Done
+ return propertyClassEntity;
+ }
+
+ @Override
+ protected PropertyClassEntity createClass(Class> value)
+ {
+ PropertyClassEntity propertyClassEntity = new PropertyClassEntity();
+ propertyClassEntity.setJavaClass(value);
+ propertyClassEntity.setVersion(VERSION_ONE);
+ Long id = (Long) template.insert(INSERT_PROPERTY_CLASS, propertyClassEntity);
+ propertyClassEntity.setId(id);
+ // Done
+ return propertyClassEntity;
+ }
+}