mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
PropertyValueDAO and alf_prop_class implementation
- Contains patch that is incomplete i.e. future DAO unit tests won't work for incremental dev upgrades git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@15405 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -78,6 +78,7 @@
|
||||
<value>classpath:alfresco/dbscripts/create/3.0/${db.script.dialect}/create-activities-extras.sql</value>
|
||||
<value>classpath:alfresco/dbscripts/create/3.2/${db.script.dialect}/AlfrescoPostCreate-3.2-LockTables.sql</value>
|
||||
<value>classpath:alfresco/dbscripts/create/3.2/${db.script.dialect}/AlfrescoPostCreate-3.2-ContentTables.sql</value>
|
||||
<value>classpath:alfresco/dbscripts/create/3.3/${db.script.dialect}/AlfrescoPostCreate-3.3-PropertyValueTables.sql</value>
|
||||
</list>
|
||||
</property>
|
||||
<property name="validateUpdateScriptPatches">
|
||||
@@ -97,6 +98,7 @@
|
||||
<ref bean="patch.db-V2.2-Person-2" />
|
||||
<ref bean="patch.db-V3.2-LockTables" />
|
||||
<ref bean="patch.db-V3.2-ContentTables" />
|
||||
<ref bean="patch.db-V3.3-PropertyValueTables" />
|
||||
</list>
|
||||
</property>
|
||||
<property name="postUpdateScriptPatches">
|
||||
|
@@ -207,6 +207,42 @@
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<!-- ===================================== -->
|
||||
<!-- ID lookup for general, shared, immutable entities -->
|
||||
<!-- ===================================== -->
|
||||
|
||||
<!-- The cross-transaction shared cache for Encoding entities -->
|
||||
|
||||
<bean name="immutableEntitySharedCache" class="org.alfresco.repo.cache.EhCacheAdapter">
|
||||
<property name="cache">
|
||||
<bean class="org.springframework.cache.ehcache.EhCacheFactoryBean" >
|
||||
<property name="cacheManager">
|
||||
<ref bean="internalEHCacheManager" />
|
||||
</property>
|
||||
<property name="cacheName">
|
||||
<value>org.alfresco.cache.immutableEntityCache</value>
|
||||
</property>
|
||||
</bean>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<!-- The transactional cache for Encoding entities -->
|
||||
|
||||
<bean name="immutableEntityCache" class="org.alfresco.repo.cache.TransactionalCache">
|
||||
<property name="sharedCache">
|
||||
<ref bean="immutableEntitySharedCache" />
|
||||
</property>
|
||||
<property name="cacheManager" >
|
||||
<ref bean="transactionalEHCacheManager" />
|
||||
</property>
|
||||
<property name="name">
|
||||
<value>org.alfresco.cache.immutableEntityTransactionalCache</value>
|
||||
</property>
|
||||
<property name="maxCacheSize">
|
||||
<value>100</value>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<!-- =============================== -->
|
||||
<!-- Store and Node ID cache lookup -->
|
||||
<!-- =============================== -->
|
||||
|
@@ -43,4 +43,9 @@
|
||||
<property name="sqlMapClientTemplate" ref="contentSqlMapClientTemplate"/>
|
||||
</bean>
|
||||
|
||||
<bean id="propertyValueDAO" class="org.alfresco.repo.domain.propval.ibatis.PropertyValueDAOImpl">
|
||||
<property name="sqlMapClientTemplate" ref="propertyValueSqlMapClientTemplate"/>
|
||||
<property name="propertyClassCache" ref="immutableEntityCache"/>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
|
@@ -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'
|
||||
);
|
@@ -296,6 +296,18 @@
|
||||
eternal="true"
|
||||
overflowToDisk="false"
|
||||
/>
|
||||
<cache
|
||||
name="org.alfresco.cache.encodingEntityCache"
|
||||
maxElementsInMemory="1000"
|
||||
eternal="true"
|
||||
overflowToDisk="false"
|
||||
/>
|
||||
<cache
|
||||
name="org.alfresco.cache.immutableEntityCache"
|
||||
maxElementsInMemory="10000"
|
||||
eternal="true"
|
||||
overflowToDisk="false"
|
||||
/>
|
||||
<cache
|
||||
name="org.alfresco.cache.storeAndNodeIdCache"
|
||||
maxElementsInMemory="10000"
|
||||
|
@@ -74,4 +74,16 @@
|
||||
<property name="sqlMapClient" ref="contentSqlMapClient"/>
|
||||
</bean>
|
||||
|
||||
<!-- iBatis config for PropertyValue domain -->
|
||||
<bean id="propertyValueSqlMapClient" class="org.alfresco.ibatis.HierarchicalSqlMapClientFactoryBean">
|
||||
<property name="dataSource" ref="dataSource"/>
|
||||
<property name="resourceLoader" ref="dialectResourceLoader" />
|
||||
<property name="configLocation">
|
||||
<value>classpath:alfresco/ibatis/propval-SqlMapConfig.xml</value>
|
||||
</property>
|
||||
</bean>
|
||||
<bean id="propertyValueSqlMapClientTemplate" class="org.springframework.orm.ibatis.SqlMapClientTemplate">
|
||||
<property name="sqlMapClient" ref="propertyValueSqlMapClient"/>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
|
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<!DOCTYPE sqlMap
|
||||
PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
|
||||
"http://ibatis.apache.org/dtd/sql-map-2.dtd">
|
||||
|
||||
<sqlMap namespace="alfresco.propval">
|
||||
|
||||
<!-- -->
|
||||
<!-- Type Defs -->
|
||||
<!-- -->
|
||||
|
||||
<typeAlias alias="PropertyClass" type="org.alfresco.repo.domain.propval.PropertyClassEntity"/>
|
||||
|
||||
<!-- -->
|
||||
<!-- Result Maps -->
|
||||
<!-- -->
|
||||
|
||||
<resultMap id="result.PropertyClass" class="PropertyClass">
|
||||
<result property="id" column="id" jdbcType="BIGINT" javaType="java.lang.Long"/>
|
||||
<result property="version" column="version" jdbcType="BIGINT" javaType="java.lang.Long"/>
|
||||
<result property="javaClassName" column="java_class_name" jdbcType="VARCHAR" javaType="java.lang.String"/>
|
||||
<result property="javaClassNameShort" column="java_class_name_short" jdbcType="VARCHAR" javaType="java.lang.String"/>
|
||||
<result property="javaClassNameCrc" column="java_class_name_crc" jdbcType="BIGINT" javaType="java.lang.Long"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- -->
|
||||
<!-- Parameter Maps -->
|
||||
<!-- -->
|
||||
|
||||
<!--
|
||||
<parameterMap id="parameter.ExclusiveLockUpdateMap" class="map">
|
||||
<parameter property="newLockToken" jdbcType="VARCHAR" javaType="java.lang.String"/>
|
||||
<parameter property="newStartTime" jdbcType="BIGINT" javaType="java.lang.Long"/>
|
||||
<parameter property="newExpiryTime" jdbcType="BIGINT" javaType="java.lang.Long"/>
|
||||
<parameter property="exclusiveLockResourceId" jdbcType="BIGINT" javaType="java.lang.Long"/>
|
||||
<parameter property="oldLockToken" jdbcType="VARCHAR" javaType="java.lang.String"/>
|
||||
</parameterMap>
|
||||
-->
|
||||
|
||||
<!-- -->
|
||||
<!-- SQL Snippets -->
|
||||
<!-- -->
|
||||
|
||||
<sql id="insert.PropertyClass.AutoIncrement">
|
||||
insert into alf_prop_class (version, java_class_name, java_class_name_short, java_class_name_crc)
|
||||
values (#version#, #javaClassName#, #javaClassNameShort#, #javaClassNameCrc#)
|
||||
</sql>
|
||||
|
||||
<!-- -->
|
||||
<!-- Statements -->
|
||||
<!-- -->
|
||||
|
||||
<!-- Get a property class by ID -->
|
||||
<select id="select.PropertyClassByID" parameterClass="PropertyClass" resultMap="result.PropertyClass">
|
||||
select
|
||||
*
|
||||
from
|
||||
alf_prop_class
|
||||
where
|
||||
id = #id#
|
||||
</select>
|
||||
|
||||
<!-- Get the property class by class name -->
|
||||
<select id="select.PropertyClassByName" parameterClass="PropertyClass" resultMap="result.PropertyClass">
|
||||
select
|
||||
*
|
||||
from
|
||||
alf_prop_class
|
||||
where
|
||||
java_class_name_crc = #javaClassNameCrc# and
|
||||
java_class_name_short = #javaClassNameShort#
|
||||
</select>
|
||||
|
||||
</sqlMap>
|
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<!DOCTYPE sqlMap
|
||||
PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
|
||||
"http://ibatis.apache.org/dtd/sql-map-2.dtd">
|
||||
|
||||
<sqlMap namespace="alfresco.propval">
|
||||
|
||||
<insert id="insert.PropertyClass" parameterClass="PropertyClass" >
|
||||
<include refid="insert.PropertyClass.AutoIncrement"/>
|
||||
<selectKey resultClass="long" keyProperty="id" type="post">
|
||||
KEY_COLUMN:GENERATED_KEY
|
||||
</selectKey>
|
||||
</insert>
|
||||
|
||||
</sqlMap>
|
12
config/alfresco/ibatis/propval-SqlMapConfig.xml
Normal file
12
config/alfresco/ibatis/propval-SqlMapConfig.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<!DOCTYPE sqlMapConfig
|
||||
PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"
|
||||
"http://ibatis.apache.org/dtd/sql-map-config-2.dtd">
|
||||
|
||||
<sqlMapConfig>
|
||||
|
||||
<sqlMap resource="alfresco/ibatis/#resource.dialect#/propval-common-SqlMap.xml"/>
|
||||
<sqlMap resource="alfresco/ibatis/#resource.dialect#/propval-insert-SqlMap.xml"/>
|
||||
|
||||
</sqlMapConfig>
|
@@ -1964,4 +1964,16 @@
|
||||
<property name="scriptsACP"><value>alfresco/templates/imap/command_processor_scripts.acp</value></property>
|
||||
</bean>
|
||||
|
||||
<bean id="patch.db-V3.3-PropertyValueTables" class="org.alfresco.repo.admin.patch.impl.SchemaUpgradeScriptPatch" parent="basePatch">
|
||||
<property name="id"><value>patch.db-V3.3-PropertyValueTables</value></property>
|
||||
<property name="description"><value>patch.schemaUpgradeScript.description</value></property>
|
||||
<property name="fixesFromSchema"><value>0</value></property>
|
||||
<property name="fixesToSchema"><value>3000</value></property>
|
||||
<property name="targetSchema"><value>3001</value></property>
|
||||
<property name="scriptUrl">
|
||||
<!-- Share a create script -->
|
||||
<value>classpath:alfresco/dbscripts/create/3.3/${db.script.dialect}/AlfrescoPostCreate-3.3-PropertyValueTables.sql</value>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
|
@@ -19,4 +19,4 @@ version.build=@build-number@
|
||||
|
||||
# Schema number
|
||||
|
||||
version.schema=3000
|
||||
version.schema=3001
|
||||
|
@@ -35,28 +35,19 @@ import org.alfresco.util.Pair;
|
||||
* <p>
|
||||
* The keys must have good <code>equals</code> and </code>hashCode</code> implementations and
|
||||
* must respect the case-sensitivity of the use-case.
|
||||
* <p>
|
||||
* 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<K extends Serializable, V extends Object, VK extends Serializable>
|
||||
{
|
||||
private static final String NULL_VALUE = "@@NULL_VALUE@@";
|
||||
|
||||
private final SimpleCache<Serializable, Object> cache;
|
||||
private final EntityLookup<K, V, VK> entityLookup;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public EntityLookupCache(SimpleCache cache, EntityLookup<K, V, VK> entityLookup)
|
||||
{
|
||||
this.cache = cache;
|
||||
this.entityLookup = entityLookup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface to support lookups of the entities using keys and values.
|
||||
*/
|
||||
public static interface EntityLookup<K1 extends Serializable, V1 extends Object, VK1 extends Serializable>
|
||||
public static interface EntityLookupCallbackDAO<K1 extends Serializable, V1 extends Object, VK1 extends Serializable>
|
||||
{
|
||||
/**
|
||||
* 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<K extends Serializable, V extends Object, VK exte
|
||||
Pair<K1, V1> createValue(V1 value);
|
||||
}
|
||||
|
||||
private static final String NULL_VALUE = "@@NULL_VALUE@@";
|
||||
private static final String CACHE_REGION_DEFAULT = "DEFAULT";
|
||||
|
||||
private final SimpleCache<Serializable, Object> cache;
|
||||
private final EntityLookupCallbackDAO<K, V, VK> 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<K, V> getByKey(K key)
|
||||
public EntityLookupCache(SimpleCache cache, EntityLookupCallbackDAO<K, V, VK> entityLookup)
|
||||
{
|
||||
this(cache, CACHE_REGION_DEFAULT, entityLookup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the lookup cache, using the given cache region.
|
||||
* <p>
|
||||
* 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<K, V, VK> entityLookup)
|
||||
{
|
||||
this.cache = cache;
|
||||
this.entityLookup = entityLookup;
|
||||
this.cacheRegion = cacheRegion;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Pair<K, V> 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<K extends Serializable, V extends Object, VK exte
|
||||
if (entityPair == null)
|
||||
{
|
||||
// Cache nulls
|
||||
cache.put(key, NULL_VALUE);
|
||||
cache.put(cacheKey, NULL_VALUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cache the value
|
||||
cache.put(key, entityPair.getSecond());
|
||||
cache.put(cacheKey, entityPair.getSecond());
|
||||
}
|
||||
// Done
|
||||
return entityPair;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Pair<K, V> getByValue(V value)
|
||||
public Pair<K, V> 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<K extends Serializable, V extends Object, VK exte
|
||||
if (entityPair == null)
|
||||
{
|
||||
// Cache a null
|
||||
cache.put(valueKey, NULL_VALUE);
|
||||
cache.put(cacheKey, NULL_VALUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
key = entityPair.getFirst();
|
||||
// Cache the key
|
||||
cache.put(valueKey, key);
|
||||
cache.put(cacheKey, key);
|
||||
}
|
||||
// Done
|
||||
return entityPair;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Pair<K, V> getOrCreateByValue(V value)
|
||||
public Pair<K, V> 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<K extends Serializable, V extends Object, VK exte
|
||||
}
|
||||
key = entityPair.getFirst();
|
||||
// Cache the key and value
|
||||
cache.put(valueKey, key);
|
||||
cache.put(key, value);
|
||||
cache.put(cacheKey, key);
|
||||
cache.put(new CacheRegionKey(cacheRegion, key), value);
|
||||
// Done
|
||||
return entityPair;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void remove(K key)
|
||||
{
|
||||
CacheRegionKey keyCacheKey = new CacheRegionKey(cacheRegion, key);
|
||||
V value = (V) cache.get(keyCacheKey);
|
||||
if (value != null && !value.equals(NULL_VALUE))
|
||||
{
|
||||
// Get the value key and remove it
|
||||
VK valueKey = entityLookup.getValueKey(value);
|
||||
CacheRegionKey valueCacheKey = new CacheRegionKey(cacheRegion, valueKey);
|
||||
cache.remove(valueCacheKey);
|
||||
}
|
||||
cache.remove(keyCacheKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Key-wrapper used to separate cache regions, allowing a single cache to be used for different
|
||||
* purposes.
|
||||
*/
|
||||
private static class CacheRegionKey implements Serializable
|
||||
{
|
||||
private static final long serialVersionUID = -213050301938804468L;
|
||||
|
||||
private final String cacheRegion;
|
||||
private final Serializable cacheKey;
|
||||
private final int hashCode;
|
||||
private CacheRegionKey(String cacheRegion, Serializable cacheKey)
|
||||
{
|
||||
this.cacheRegion = cacheRegion;
|
||||
this.cacheKey = cacheKey;
|
||||
this.hashCode = cacheRegion.hashCode() + cacheKey.hashCode();
|
||||
}
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return cacheRegion + "." + cacheKey.toString();
|
||||
}
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (!(obj instanceof CacheRegionKey))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
CacheRegionKey that = (CacheRegionKey) obj;
|
||||
return this.cacheRegion.equals(that.cacheRegion) && this.cacheKey.equals(that.cacheKey);
|
||||
}
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -32,7 +32,7 @@ import junit.framework.TestCase;
|
||||
|
||||
import org.alfresco.repo.cache.MemoryCache;
|
||||
import org.alfresco.repo.cache.SimpleCache;
|
||||
import org.alfresco.repo.cache.lookup.EntityLookupCache.EntityLookup;
|
||||
import org.alfresco.repo.cache.lookup.EntityLookupCache.EntityLookupCallbackDAO;
|
||||
import org.alfresco.util.Pair;
|
||||
|
||||
/**
|
||||
@@ -45,16 +45,19 @@ import org.alfresco.util.Pair;
|
||||
* @author Derek Hulley
|
||||
* @since 3.3
|
||||
*/
|
||||
public class EntityLookupCacheTest extends TestCase implements EntityLookup<Long, Object, String>
|
||||
public class EntityLookupCacheTest extends TestCase implements EntityLookupCallbackDAO<Long, Object, String>
|
||||
{
|
||||
private EntityLookupCache<Long, Object, String> entityLookupCache;
|
||||
SimpleCache<Long, Object> cache;
|
||||
private EntityLookupCache<Long, Object, String> entityLookupCacheA;
|
||||
private EntityLookupCache<Long, Object, String> entityLookupCacheB;
|
||||
private TreeMap<Long, String> database;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception
|
||||
{
|
||||
SimpleCache<Long, Object> cache = new MemoryCache<Long, Object>();
|
||||
entityLookupCache = new EntityLookupCache<Long, Object, String>(cache, this);
|
||||
cache = new MemoryCache<Long, Object>();
|
||||
entityLookupCacheA = new EntityLookupCache<Long, Object, String>(cache, "A", this);
|
||||
entityLookupCacheB = new EntityLookupCache<Long, Object, String>(cache, "B", this);
|
||||
database = new TreeMap<Long, String>();
|
||||
}
|
||||
|
||||
@@ -63,7 +66,7 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookup<Long
|
||||
try
|
||||
{
|
||||
// Keep the "database" empty
|
||||
entityLookupCache.getByValue(this);
|
||||
entityLookupCacheA.getByValue(this);
|
||||
}
|
||||
catch (AssertionFailedError e)
|
||||
{
|
||||
@@ -74,28 +77,28 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookup<Long
|
||||
public void testLookupAgainstEmpty() throws Exception
|
||||
{
|
||||
TestValue value = new TestValue("AAA");
|
||||
Pair<Long, Object> entityPair = entityLookupCache.getByValue(value);
|
||||
Pair<Long, Object> 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<Long
|
||||
createValue(new TestValue("CCC"));
|
||||
|
||||
// Look up by value
|
||||
Pair<Long, Object> entityPair = entityLookupCache.getByValue(new TestValue("AAA"));
|
||||
Pair<Long, Object> 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<Long, Object> entityPairAAA = entityLookupCacheA.getOrCreateByValue(valueAAA);
|
||||
assertNotNull(entityPairAAA);
|
||||
assertEquals("AAA", database.get(entityPairAAA.getFirst()));
|
||||
assertEquals(2, cache.getKeys().size());
|
||||
|
||||
TestValue valueBBB = new TestValue("BBB");
|
||||
Pair<Long, Object> 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
|
||||
*/
|
||||
|
119
source/java/org/alfresco/repo/domain/CrcHelper.java
Normal file
119
source/java/org/alfresco/repo/domain/CrcHelper.java
Normal file
@@ -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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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 <b>NULL</b>.
|
||||
*
|
||||
* @param value the raw value that will be persisted
|
||||
* @param dataLength the maximum number of characters that can be persisted
|
||||
* @param useCharsFromStart <tt>true</tt> if the shortened string value must be made from
|
||||
* the first characters of the string or <tt>false</tt> to use
|
||||
* characters from the end of the string.
|
||||
* @param caseSensitive <tt>true</tt> if the resulting pair must be case-sensitive or
|
||||
* <tt>false</tt> if the pair must be case-insensitive.
|
||||
* @return Return the persistable pair. The result will never be <tt>null</tt>,
|
||||
* but the individual pair values will be <tt>null</tt> if the
|
||||
* value given is <tt>null</tt>
|
||||
*/
|
||||
public static Pair<String, Long> getStringCrcPair(
|
||||
String value,
|
||||
int dataLength,
|
||||
boolean useCharsFromStart,
|
||||
boolean caseSensitive)
|
||||
{
|
||||
String valueLowerCase;
|
||||
if (value == null)
|
||||
{
|
||||
return new Pair<String, Long>(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<String, Long>(valueShort, valueCrc);
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
|
@@ -24,9 +24,7 @@
|
||||
*/
|
||||
package org.alfresco.repo.domain.contentdata;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.zip.CRC32;
|
||||
|
||||
import org.alfresco.repo.domain.CrcHelper;
|
||||
import org.alfresco.util.EqualsHelper;
|
||||
import org.alfresco.util.Pair;
|
||||
|
||||
@@ -98,51 +96,7 @@ public class ContentUrlEntity
|
||||
*/
|
||||
private static Pair<String, Long> getContentUrlCrcPair(String internalContentUrl)
|
||||
{
|
||||
if (internalContentUrl == null)
|
||||
{
|
||||
return new Pair<String, Long>(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<String, Long>(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 CrcHelper.getStringCrcPair(internalContentUrl, 12, false, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -195,8 +149,7 @@ public class ContentUrlEntity
|
||||
{
|
||||
this.contentUrl = contentUrl;
|
||||
// Convert the URL to a persistable value
|
||||
String internalContentUrl = ContentUrlEntity.getInternalUrl(contentUrl);
|
||||
Pair<String, Long> contentUrlPair = ContentUrlEntity.getContentUrlCrcPair(internalContentUrl);
|
||||
Pair<String, Long> contentUrlPair = ContentUrlEntity.getContentUrlCrcPair(contentUrl);
|
||||
this.contentUrlShort = contentUrlPair.getFirst();
|
||||
this.contentUrlCrc = contentUrlPair.getSecond();
|
||||
}
|
||||
|
@@ -43,7 +43,6 @@ import org.alfresco.util.ParameterCheck;
|
||||
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<Serializable, Serializable> encodingEntityCache;
|
||||
|
||||
/**
|
||||
|
@@ -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.
|
||||
* <p>
|
||||
* 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<Long, Class<?>, String> propertyClassCache;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param propertyClassCache the cache of IDs to property classes
|
||||
*/
|
||||
public void setPropertyClassCache(SimpleCache<Serializable, Object> propertyClassCache)
|
||||
{
|
||||
PropertyValueCallbackDAO daoCallback = new PropertyValueCallbackDAO();
|
||||
this.propertyClassCache = new EntityLookupCache<Long, Class<?>, String>(
|
||||
propertyClassCache,
|
||||
CACHE_REGION_PROPERTY_CLASS,
|
||||
daoCallback);
|
||||
}
|
||||
|
||||
public Pair<Long, Class<?>> getPropertyClass(Long id)
|
||||
{
|
||||
Pair<Long, Class<?>> entityPair = propertyClassCache.getByKey(id);
|
||||
if (entityPair == null)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("No property class exists for ID " + id);
|
||||
}
|
||||
return entityPair;
|
||||
}
|
||||
|
||||
public Pair<Long, Class<?>> getPropertyClass(Class<?> clazz)
|
||||
{
|
||||
Pair<Long, Class<?>> entityPair = propertyClassCache.getByValue(clazz);
|
||||
return entityPair;
|
||||
}
|
||||
|
||||
public Pair<Long, Class<?>> getOrCreatePropertyClass(Class<?> clazz)
|
||||
{
|
||||
Pair<Long, Class<?>> entityPair = propertyClassCache.getOrCreateByValue(clazz);
|
||||
return entityPair;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for <b>alf_prop_type</b> DAO.
|
||||
*/
|
||||
private class PropertyValueCallbackDAO implements EntityLookupCallbackDAO<Long, Class<?>, String>
|
||||
{
|
||||
private final Pair<Long, Class<?>> convertEntityToPair(PropertyClassEntity propertyClassEntity)
|
||||
{
|
||||
if (propertyClassEntity == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return propertyClassEntity.getEntityPair();
|
||||
}
|
||||
}
|
||||
|
||||
public String getValueKey(Class<?> value)
|
||||
{
|
||||
return value.getName();
|
||||
}
|
||||
|
||||
public Pair<Long, Class<?>> createValue(Class<?> value)
|
||||
{
|
||||
PropertyClassEntity propertyClassEntity = createClass(value);
|
||||
return convertEntityToPair(propertyClassEntity);
|
||||
}
|
||||
|
||||
public Pair<Long, Class<?>> findByKey(Long key)
|
||||
{
|
||||
PropertyClassEntity propertyClassEntity = findClassById(key);
|
||||
return convertEntityToPair(propertyClassEntity);
|
||||
}
|
||||
|
||||
public Pair<Long, Class<?>> 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);
|
||||
}
|
@@ -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 <b>alf_prop_class</b> 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<Long, Class<?>> getEntityPair()
|
||||
{
|
||||
return new Pair<Long, Class<?>>(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<String, Long> 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;
|
||||
}
|
||||
}
|
@@ -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 <b>alf_prop_XXX</b> tables.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 3.3
|
||||
*/
|
||||
public interface PropertyValueDAO
|
||||
{
|
||||
Pair<Long, Class<?>> getPropertyClass(Class<?> clazz);
|
||||
|
||||
Pair<Long, Class<?>> getPropertyClass(Long id);
|
||||
|
||||
Pair<Long, Class<?>> getOrCreatePropertyClass(Class<?> clazz);
|
||||
}
|
@@ -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<Pair<Long, Class<?>>> createClassCallback = new RetryingTransactionCallback<Pair<Long, Class<?>>>()
|
||||
{
|
||||
public Pair<Long, Class<?>> execute() throws Throwable
|
||||
{
|
||||
// Get the classes
|
||||
return propertyValueDAO.getOrCreatePropertyClass(clazz);
|
||||
}
|
||||
};
|
||||
final Pair<Long, Class<?>> clazzEntityPair = txnHelper.doInTransaction(createClassCallback, false);
|
||||
assertNotNull(clazzEntityPair);
|
||||
assertNotNull(clazzEntityPair.getFirst());
|
||||
assertEquals(clazz, clazzEntityPair.getSecond());
|
||||
// Now retrieve it
|
||||
RetryingTransactionCallback<Void> getClassCallback = new RetryingTransactionCallback<Void>()
|
||||
{
|
||||
public Void execute() throws Throwable
|
||||
{
|
||||
Pair<Long, Class<?>> checkPair1 = propertyValueDAO.getPropertyClass(clazzEntityPair.getFirst());
|
||||
assertEquals(clazzEntityPair, checkPair1);
|
||||
Pair<Long, Class<?>> checkPair2 = propertyValueDAO.getPropertyClass(clazzEntityPair.getSecond());
|
||||
assertEquals(clazzEntityPair, checkPair2);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
txnHelper.doInTransaction(getClassCallback, true);
|
||||
|
||||
// Test failure when requesting invalid ID
|
||||
RetryingTransactionCallback<Void> badGetCallback = new RetryingTransactionCallback<Void>()
|
||||
{
|
||||
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<Void> noHitCallback = new RetryingTransactionCallback<Void>()
|
||||
{
|
||||
public Void execute() throws Throwable
|
||||
{
|
||||
propertyValueDAO.getPropertyClass(this.getClass());
|
||||
propertyValueDAO.getPropertyClass(this.getClass());
|
||||
return null;
|
||||
}
|
||||
};
|
||||
txnHelper.doInTransaction(noHitCallback, false);
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user