diff --git a/config/alfresco/bootstrap-context.xml b/config/alfresco/bootstrap-context.xml index b35ea138b0..4be2289233 100644 --- a/config/alfresco/bootstrap-context.xml +++ b/config/alfresco/bootstrap-context.xml @@ -78,6 +78,7 @@ classpath:alfresco/dbscripts/create/3.0/${db.script.dialect}/create-activities-extras.sql classpath:alfresco/dbscripts/create/3.2/${db.script.dialect}/AlfrescoPostCreate-3.2-LockTables.sql classpath:alfresco/dbscripts/create/3.2/${db.script.dialect}/AlfrescoPostCreate-3.2-ContentTables.sql + classpath:alfresco/dbscripts/create/3.3/${db.script.dialect}/AlfrescoPostCreate-3.3-PropertyValueTables.sql @@ -97,6 +98,7 @@ + diff --git a/config/alfresco/cache-context.xml b/config/alfresco/cache-context.xml index 2fc1726379..24ad2f4ec0 100644 --- a/config/alfresco/cache-context.xml +++ b/config/alfresco/cache-context.xml @@ -207,6 +207,42 @@ + + + + + + + + + + + + + + org.alfresco.cache.immutableEntityCache + + + + + + + + + + + + + + + + org.alfresco.cache.immutableEntityTransactionalCache + + + 100 + + + @@ -459,76 +495,76 @@ 10 - - - - - - - - - - - - - - - org.alfresco.cache.sysAdminCache - - - - - - - - - - - - - - org.alfresco.sysAdminTransactionalCache - - - 10 - - + + - - - - - - - - - - - - - org.alfresco.repo.avm.storeLookupSharedCache - - - - - - - - - - - - - - - - org.alfresco.repo.avm.storeLookupTransactionalCache - - - 200 - - + + + + + + + + + + + org.alfresco.cache.sysAdminCache + + + + + + + + + + + + + + org.alfresco.sysAdminTransactionalCache + + + 10 + + + + + + + + + + + + + + + + + org.alfresco.repo.avm.storeLookupSharedCache + + + + + + + + + + + + + + + + org.alfresco.repo.avm.storeLookupTransactionalCache + + + 200 + + @@ -602,7 +638,7 @@ 10000 - + diff --git a/config/alfresco/dao/dao-context.xml b/config/alfresco/dao/dao-context.xml index f6eb59da1f..3c1de69b01 100644 --- a/config/alfresco/dao/dao-context.xml +++ b/config/alfresco/dao/dao-context.xml @@ -15,10 +15,10 @@ - + - - + + @@ -43,4 +43,9 @@ + + + + + diff --git a/config/alfresco/dbscripts/create/3.3/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoPostCreate-3.3-PropertyValueTables.sql b/config/alfresco/dbscripts/create/3.3/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoPostCreate-3.3-PropertyValueTables.sql new file mode 100644 index 0000000000..3937309b77 --- /dev/null +++ b/config/alfresco/dbscripts/create/3.3/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoPostCreate-3.3-PropertyValueTables.sql @@ -0,0 +1,32 @@ +-- +-- Title: Property Value tables +-- Database: MySQL InnoDB +-- Since: V3.3 Schema 3001 +-- Author: Derek Hulley +-- +-- Please contact support@alfresco.com if you need assistance with the upgrade. +-- + +CREATE TABLE alf_prop_class +( + id BIGINT NOT NULL AUTO_INCREMENT, + version BIGINT NOT NULL, + java_class_name VARCHAR(255) NOT NULL, + java_class_name_short VARCHAR(32) NOT NULL, + java_class_name_crc BIGINT NOT NULL, + UNIQUE INDEX idx_prop_class_crc (java_class_name_crc, java_class_name_short), + INDEX idx_prop_class_class (java_class_name), + PRIMARY KEY (id) +) ENGINE=InnoDB; + +-- +-- Record script finish +-- +DELETE FROM alf_applied_patch WHERE id = 'patch.db-V3.3-PropertyValueTables'; +INSERT INTO alf_applied_patch + (id, description, fixes_from_schema, fixes_to_schema, applied_to_schema, target_schema, applied_on_date, applied_to_server, was_executed, succeeded, report) + VALUES + ( + 'patch.db-V3.3-PropertyValueTables', 'Manually executed script upgrade V3.3: PropertyValue Tables', + 0, 3000, -1, 3001, null, 'UNKOWN', 1, 1, 'Script completed' + ); \ No newline at end of file diff --git a/config/alfresco/ehcache-default.xml b/config/alfresco/ehcache-default.xml index d312e986b8..3d8c7a8eb8 100644 --- a/config/alfresco/ehcache-default.xml +++ b/config/alfresco/ehcache-default.xml @@ -296,6 +296,18 @@ eternal="true" overflowToDisk="false" /> + + + + + + + + classpath:alfresco/ibatis/propval-SqlMapConfig.xml + + + + + + diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/propval-common-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/propval-common-SqlMap.xml new file mode 100644 index 0000000000..91be74448c --- /dev/null +++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/propval-common-SqlMap.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + insert into alf_prop_class (version, java_class_name, java_class_name_short, java_class_name_crc) + values (#version#, #javaClassName#, #javaClassNameShort#, #javaClassNameCrc#) + + + + + + + + + + + + + \ No newline at end of file diff --git a/config/alfresco/ibatis/org.hibernate.dialect.MySQLInnoDBDialect/propval-insert-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.MySQLInnoDBDialect/propval-insert-SqlMap.xml new file mode 100644 index 0000000000..6eb4a28c5d --- /dev/null +++ b/config/alfresco/ibatis/org.hibernate.dialect.MySQLInnoDBDialect/propval-insert-SqlMap.xml @@ -0,0 +1,16 @@ + + + + + + + + + + KEY_COLUMN:GENERATED_KEY + + + + \ No newline at end of file diff --git a/config/alfresco/ibatis/propval-SqlMapConfig.xml b/config/alfresco/ibatis/propval-SqlMapConfig.xml new file mode 100644 index 0000000000..422e5800b9 --- /dev/null +++ b/config/alfresco/ibatis/propval-SqlMapConfig.xml @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/config/alfresco/patch/patch-services-context.xml b/config/alfresco/patch/patch-services-context.xml index 9ac3ad18f4..7115662906 100644 --- a/config/alfresco/patch/patch-services-context.xml +++ b/config/alfresco/patch/patch-services-context.xml @@ -1964,4 +1964,16 @@ alfresco/templates/imap/command_processor_scripts.acp + + patch.db-V3.3-PropertyValueTables + patch.schemaUpgradeScript.description + 0 + 3000 + 3001 + + + classpath:alfresco/dbscripts/create/3.3/${db.script.dialect}/AlfrescoPostCreate-3.3-PropertyValueTables.sql + + + diff --git a/config/alfresco/version.properties b/config/alfresco/version.properties index 8cf156aa33..72bbc71928 100644 --- a/config/alfresco/version.properties +++ b/config/alfresco/version.properties @@ -19,4 +19,4 @@ version.build=@build-number@ # Schema number -version.schema=3000 +version.schema=3001 diff --git a/source/java/org/alfresco/repo/cache/lookup/EntityLookupCache.java b/source/java/org/alfresco/repo/cache/lookup/EntityLookupCache.java index 2bf547ed4a..6c9b28817a 100644 --- a/source/java/org/alfresco/repo/cache/lookup/EntityLookupCache.java +++ b/source/java/org/alfresco/repo/cache/lookup/EntityLookupCache.java @@ -35,28 +35,19 @@ import org.alfresco.util.Pair; *

* 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; + } +}