mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
This checks in the current state of WCM development.
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/WCM-DEV2/root@3128 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -1,50 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
|
||||
<hibernate-mapping package="org.alfresco.repo.avm.hibernate">
|
||||
<hibernate-mapping package="org.alfresco.repo.avm">
|
||||
<!-- AVMNodeBean is the abstract base for filesystem like objects.
|
||||
We're using the one table per class hierarchy strategy to implement
|
||||
polymorphism. -->
|
||||
<class table="avm_nodes" abstract="true"
|
||||
name="AVMNodeBeanImpl"
|
||||
proxy="AVMNodeBean"
|
||||
optimistic-lock="version" lazy="true">
|
||||
name="AVMNodeImpl"
|
||||
proxy="AVMNode"
|
||||
optimistic-lock="version"
|
||||
lazy="true">
|
||||
<cache usage="read-write"/>
|
||||
<!-- The id is set programmatically using an Issuer. See below. -->
|
||||
<id name="id" column="id" type="long"/>
|
||||
<id name="id"
|
||||
column="id"
|
||||
type="long"/>
|
||||
<!-- I suppose this would be more efficient to encode type by an int.
|
||||
We'll see if using a string makes a difference. -->
|
||||
<discriminator column="class_type" type="string" length="20"/>
|
||||
<discriminator column="class_type"
|
||||
type="string"
|
||||
length="20"/>
|
||||
<!-- We're using hibernate's own versioning scheme for concurrency control.
|
||||
I don't know how well that will play with a full Spring-JTA stack. -->
|
||||
<version column="vers" name="vers" type="long"/>
|
||||
<version column="vers"
|
||||
name="vers"
|
||||
type="long"/>
|
||||
<!-- BasicAttributes are attributes that pretty much all AVMNodes will
|
||||
have. -->
|
||||
<many-to-one name="ancestor" column="ancestor_id"
|
||||
class="AVMNodeBeanImpl"/>
|
||||
class="AVMNodeImpl"/>
|
||||
<!-- Nothing does anything with this yet. We'll need it for
|
||||
complete versioning semantics. -->
|
||||
<many-to-one name="mergedFrom" column="merged_from"
|
||||
class="AVMNodeBeanImpl"/>
|
||||
class="AVMNodeImpl"/>
|
||||
<!-- Parent is a very specific notion of containment. The parent
|
||||
of an AVMNode is the parent in which that node was created. -->
|
||||
<many-to-one name="parent" column="parent_id"
|
||||
class="DirectoryNodeBeanImpl"/>
|
||||
class="DirectoryNodeImpl"/>
|
||||
<!-- This should really be not null, but I haven't figured out
|
||||
the right way to build the relation so that nullability constraints
|
||||
won't cause violations in the db during saves. -->
|
||||
<many-to-one name="repository" column="repository"
|
||||
class="RepositoryBeanImpl" cascade="save-update"/>
|
||||
class="RepositoryImpl"/>
|
||||
<property name="versionID" type="int" column="version_id"
|
||||
not-null="true"/>
|
||||
<!-- The branch id is always 0 for nodes that are not part of
|
||||
a branch. -->
|
||||
<property name="branchID" type="long" column="branch_id"
|
||||
not-null="true"/>
|
||||
<!-- This is, in fact a duplication of data, but an extremely
|
||||
convenient one. -->
|
||||
<property name="isNew" column="is_new" type="boolean"
|
||||
not-null="true"/>
|
||||
<component name="basicAttributes" class="BasicAttributesBeanImpl">
|
||||
<component name="basicAttributes" class="BasicAttributesImpl">
|
||||
<property name="creator" type="string" not-null="true"/>
|
||||
<property name="owner" type="string" not-null="true"/>
|
||||
<property name="lastModifier" type="string" not-null="true"/>
|
||||
@@ -52,19 +53,19 @@
|
||||
<property name="modDate" type="long" not-null="true"/>
|
||||
<property name="accessDate" type="long" not-null="true"/>
|
||||
</component>
|
||||
<property name="isRoot" column="is_root" type="boolean"/>
|
||||
<!-- Directories, two flavors. -->
|
||||
<subclass name="DirectoryNodeBeanImpl"
|
||||
proxy="DirectoryNodeBean"
|
||||
<subclass name="DirectoryNodeImpl"
|
||||
proxy="DirectoryNode"
|
||||
abstract="true"
|
||||
lazy="true">
|
||||
<!-- A Layered Directory is our smart symlink thingy. -->
|
||||
<subclass name="LayeredDirectoryNodeBeanImpl"
|
||||
proxy="LayeredDirectoryNodeBean"
|
||||
<subclass name="LayeredDirectoryNodeImpl"
|
||||
proxy="LayeredDirectoryNode"
|
||||
discriminator-value="layereddirectory" lazy="true">
|
||||
<!-- The layer id is an implementation trick to disambiguate
|
||||
exactly what layer is being refered to in various circumstances. -->
|
||||
<property name="layerID" column="layer_id" type="long"
|
||||
not-null="true" />
|
||||
<property name="layerID" column="layer_id" type="long"/>
|
||||
<!-- The is the moral equivalent of the value of a symlink. -->
|
||||
<property name="indirection" column="indirection"
|
||||
type="string" length="511" />
|
||||
@@ -72,74 +73,36 @@
|
||||
it points at (true) or inheriting what it points at from its
|
||||
container (false). -->
|
||||
<property name="primaryIndirection"
|
||||
column="primary_indirection" type="boolean" />
|
||||
column="primary_indirection" type="boolean"/>
|
||||
<!-- Map of names to DirectoryEntries. -->
|
||||
<map name="added" cascade="all" lazy="true">
|
||||
<cache usage="read-write"/>
|
||||
<key column="directory_id" />
|
||||
<map-key type="string" column="name" />
|
||||
<!-- A DirectoryEntry is a (node)type AVMNode reference pair.
|
||||
Should probably convert type into an integer code. -->
|
||||
<composite-element class="DirectoryEntry">
|
||||
<property name="type" column="type_code"
|
||||
type="int" not-null="true" />
|
||||
<many-to-one name="child"
|
||||
class="AVMNodeBeanImpl" cascade="save-update"
|
||||
not-null="true">
|
||||
</many-to-one>
|
||||
</composite-element>
|
||||
</map>
|
||||
<!-- This is just the set of names of children deleted (and therefore
|
||||
hidden) in this layer. -->
|
||||
<set name="deleted" table="deleted_children"
|
||||
fetch="join" cascade="all">
|
||||
<cache usage="read-write"/>
|
||||
<key column="directory_id" />
|
||||
<element type="string" column="name" />
|
||||
</set>
|
||||
</subclass>
|
||||
<!-- Just plain directories. -->
|
||||
<subclass name="PlainDirectoryNodeBeanImpl"
|
||||
discriminator-value="plaindirectory" proxy="PlainDirectoryNodeBean" lazy="true">
|
||||
<subclass name="PlainDirectoryNodeImpl"
|
||||
discriminator-value="plaindirectory" proxy="PlainDirectoryNode" lazy="true">
|
||||
<!-- For reference count based garbage collection of purged nodes
|
||||
to work we need to specially mark the root directory of
|
||||
a Repository as special so that its absence of parents will
|
||||
not cause it to be vacuumed away. TODO: for ease of queries,
|
||||
this probably wants to be in the base class. -->
|
||||
<property name="isRoot" column="is_root" type="boolean"/>
|
||||
<!-- A map of names to DirectoryEntries. In the AVM world, it makes sense
|
||||
that nodes don't know there own names, only their containers do. -->
|
||||
<map name="children" cascade="all" lazy="true">
|
||||
<cache usage="read-write"/>
|
||||
<key column="directory_id"/>
|
||||
<map-key type="string" column="name"/>
|
||||
<composite-element
|
||||
class="DirectoryEntry">
|
||||
<property name="type" type="int"
|
||||
not-null="true" column="type_code" />
|
||||
<many-to-one name="child"
|
||||
class="AVMNodeBeanImpl"
|
||||
not-null="true" cascade="save-update">
|
||||
</many-to-one>
|
||||
</composite-element>
|
||||
</map>
|
||||
</subclass>
|
||||
</subclass>
|
||||
<!-- There are two kinds of files, plain and symlinky. -->
|
||||
<subclass name="FileNodeBeanImpl"
|
||||
proxy="FileNodeBean"
|
||||
<subclass name="FileNodeImpl"
|
||||
proxy="FileNode"
|
||||
abstract="true"
|
||||
lazy="false">
|
||||
<!-- Plain files just have a reference to a Content object. -->
|
||||
<subclass discriminator-value="plainfile"
|
||||
name="PlainFileNodeBeanImpl" proxy="PlainFileNodeBean" lazy="true">
|
||||
name="PlainFileNodeImpl" proxy="PlainFileNode" lazy="true">
|
||||
<many-to-one name="content" column="content_id"
|
||||
class="ContentBeanImpl" fetch="join" cascade="save-update">
|
||||
class="FileContentImpl" fetch="join" cascade="save-update">
|
||||
</many-to-one>
|
||||
</subclass>
|
||||
<!-- Layered files are almost exactly copy on write symlinks. -->
|
||||
<subclass name="LayeredFileNodeBeanImpl"
|
||||
discriminator-value="layeredfile" proxy="LayeredFileNodeBean" lazy="true">
|
||||
<subclass name="LayeredFileNodeImpl"
|
||||
discriminator-value="layeredfile" proxy="LayeredFileNode" lazy="true">
|
||||
<property name="indirection" type="string" length="511"
|
||||
column="indirection" />
|
||||
</subclass>
|
||||
@@ -147,7 +110,7 @@
|
||||
</class>
|
||||
<!-- Contents are objects to hang actual bytestreams off. They are explicitly reference
|
||||
counted. -->
|
||||
<class table="contents" name="ContentBeanImpl" proxy="ContentBean"
|
||||
<class table="contents" name="FileContentImpl" proxy="FileContent"
|
||||
optimistic-lock="version">
|
||||
<cache usage="read-write" />
|
||||
<id name="id" column="id" type="long"/>
|
||||
@@ -157,51 +120,128 @@
|
||||
scheme. -->
|
||||
<property name="refCount" column="ref_count" type="int"
|
||||
not-null="true"/>
|
||||
</class>
|
||||
<!-- Issuers issue ids. They are explicit primary key generators. They seem more
|
||||
convenient than using 'native' generators, and may be more efficient. Come
|
||||
to think of it I'm not convinced that this is true. However I had a reason for
|
||||
doing things this way... -->
|
||||
<class name="Issuer" table="issuers" lazy="false"
|
||||
optimistic-lock="version">
|
||||
<cache usage="read-write" />
|
||||
<id name="name" column="name" type="string" length="20"/>
|
||||
<version name="vers" column="vers" type="long"/>
|
||||
<property name="next" column="next" type="long"
|
||||
not-null="true"/>
|
||||
</class>
|
||||
<!-- A Repository is the what we used to call a virtual repository.
|
||||
Each Repository has it's own branch ids and layer ids but shares node ids
|
||||
with other repositories. The physical repository is structured this way
|
||||
for better scaling. -->
|
||||
<class table="repositories" name="RepositoryBeanImpl"
|
||||
proxy="RepositoryBean" optimistic-lock="version">
|
||||
<cache usage="read-write" />
|
||||
<class table="repositories" name="RepositoryImpl"
|
||||
proxy="Repository" optimistic-lock="version">
|
||||
<cache usage="read-write"/>
|
||||
<id name="name" column="name" type="string"/>
|
||||
<version name="vers" column="vers" type="long"/>
|
||||
<property type="int" name="nextVersionID"
|
||||
column="next_version_id" not-null="true"/>
|
||||
<!-- Every Repository has a root directory that is the current root directory. -->
|
||||
<!-- This should be not-null but hibernate (or my own idiocy) makes that difficult. -->
|
||||
<many-to-one name="root" class="DirectoryNodeBeanImpl"
|
||||
<many-to-one name="root" class="DirectoryNodeImpl"
|
||||
column="current_root_id" unique="true" cascade="save-update">
|
||||
</many-to-one>
|
||||
<!-- In addition to the current root directory, a Repository maintains a set
|
||||
of versioned root directories. -->
|
||||
<map name="roots" table="repository_roots">
|
||||
<cache usage="read-write"/>
|
||||
<key column="repository_id"/>
|
||||
<map-key type="int" column="version_id"/>
|
||||
<many-to-many class="DirectoryNodeBeanImpl"
|
||||
column="directory_id"/>
|
||||
</map>
|
||||
<!-- NewNodes keeps track of those nodes created since the last
|
||||
'snapshot' operation. -->
|
||||
<set table="new_nodes" name="newNodes" fetch="join" lazy="true" cascade="all">
|
||||
<cache usage="read-write"/>
|
||||
<key column="repository_id"/>
|
||||
<many-to-many class="AVMNodeBeanImpl"
|
||||
column="new_node_id"/>
|
||||
</set>
|
||||
</class>
|
||||
<class name="VersionRootImpl" proxy="VersionRoot" table="version_roots">
|
||||
<!-- <cache usage="read-write"/> -->
|
||||
<id column="id" type="long">
|
||||
<generator class="native"></generator>
|
||||
</id>
|
||||
<property name="versionID" type="int" not-null="true"
|
||||
column="version_id" index="version_roots_version_id_index">
|
||||
</property>
|
||||
<property name="createDate" type="long" not-null="true" column="create_date">
|
||||
</property>
|
||||
<property name="creator" type="string" column="creator"
|
||||
not-null="true">
|
||||
</property>
|
||||
<many-to-one name="repository" column="repository_id"
|
||||
class="RepositoryImpl" not-null="true">
|
||||
</many-to-one>
|
||||
<many-to-one name="root" class="DirectoryNodeImpl"
|
||||
column="root_id" not-null="true">
|
||||
</many-to-one>
|
||||
</class>
|
||||
<class name="ChildEntryImpl" proxy="ChildEntry" table="child_entries">
|
||||
<cache usage="read-write"/>
|
||||
<composite-id>
|
||||
<key-property name="name" type="string" column="name">
|
||||
</key-property>
|
||||
<key-many-to-one name="parent" column="parent_id" class="DirectoryNodeImpl">
|
||||
</key-many-to-one>
|
||||
</composite-id>
|
||||
<many-to-one name="child" column="child_id" class="AVMNodeImpl"
|
||||
not-null="true">
|
||||
</many-to-one>
|
||||
</class>
|
||||
<class table="deleted_children" name="DeletedChildImpl" proxy="DeletedChild">
|
||||
<cache usage="read-write"/>
|
||||
<composite-id>
|
||||
<key-property name="name" column="name" type="string"/>
|
||||
<key-many-to-one name="parent" class="LayeredDirectoryNodeImpl"
|
||||
column="parent_id"/>
|
||||
</composite-id>
|
||||
</class>
|
||||
<query name="ChildEntry.ByNameParent">
|
||||
<![CDATA[
|
||||
from ChildEntryImpl ce
|
||||
where
|
||||
ce.name = :name and ce.parent = :parent
|
||||
]]>
|
||||
</query>
|
||||
<query name="ChildEntry.ByParent">
|
||||
<![CDATA[
|
||||
from ChildEntryImpl ce
|
||||
where
|
||||
ce.parent = :parent
|
||||
]]>
|
||||
</query>
|
||||
<query name="ChildEntry.ByParentChild">
|
||||
<![CDATA[
|
||||
from ChildEntryImpl ce
|
||||
where
|
||||
ce.child = :child and ce.parent = :parent
|
||||
]]>
|
||||
</query>
|
||||
<query name="AVMNode.ByNewInRepo">
|
||||
<![CDATA[
|
||||
from AVMNodeImpl a
|
||||
where
|
||||
a.repository = :repo and a.isNew = true
|
||||
]]>
|
||||
</query>
|
||||
<query name="VersionRoot.GetVersionRoot">
|
||||
<![CDATA[
|
||||
select v.root
|
||||
from VersionRootImpl v
|
||||
where
|
||||
v.repository = :rep and v.versionID = :version
|
||||
]]>
|
||||
</query>
|
||||
<query name="VersionRoot.VersionByID">
|
||||
<![CDATA[
|
||||
from VersionRootImpl v
|
||||
where
|
||||
v.repository = :rep and v.versionID = :version
|
||||
]]>
|
||||
</query>
|
||||
<query name="DeletedChild.ByNameParent">
|
||||
<![CDATA[
|
||||
from DeletedChildImpl dc
|
||||
where
|
||||
dc.parent = :parent and dc.name = :name
|
||||
]]>
|
||||
</query>
|
||||
<query name="DeletedChild.ByParent">
|
||||
<![CDATA[
|
||||
from DeletedChildImpl dc
|
||||
where
|
||||
dc.parent = :parent
|
||||
]]>
|
||||
</query>
|
||||
<query name="FindOrphans">
|
||||
<![CDATA[
|
||||
from AVMNodeImpl an
|
||||
where
|
||||
an not in ( select ce.child
|
||||
from ChildEntryImpl ce )
|
||||
and an.isRoot = false
|
||||
]]>
|
||||
</query>
|
||||
</hibernate-mapping>
|
||||
|
@@ -17,10 +17,16 @@ package org.alfresco.repo.avm.hibernate;
|
||||
* License.
|
||||
*/
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import org.alfresco.repo.avm.AVMException;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.StaleStateException;
|
||||
import org.hibernate.Transaction;
|
||||
import org.hibernate.exception.GenericJDBCException;
|
||||
|
||||
/**
|
||||
* Helper for DAOs.
|
||||
@@ -33,6 +39,16 @@ public class HibernateTxn
|
||||
*/
|
||||
private SessionFactory fSessionFactory;
|
||||
|
||||
/**
|
||||
* The random number generator for inter-retry sleep.
|
||||
*/
|
||||
private Random fRandom;
|
||||
|
||||
/**
|
||||
* The BFL.
|
||||
*/
|
||||
private ReentrantReadWriteLock fLock;
|
||||
|
||||
/**
|
||||
* Make one up.
|
||||
* @param sessionFactory The SessionFactory.
|
||||
@@ -40,52 +56,110 @@ public class HibernateTxn
|
||||
public HibernateTxn(SessionFactory sessionFactory)
|
||||
{
|
||||
fSessionFactory = sessionFactory;
|
||||
fRandom = new Random();
|
||||
fLock = new ReentrantReadWriteLock(true); // Make the lock fair.
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a set of operations under a single Hibernate transaction.
|
||||
* Keep trying if the operation fails because of a concurrency issue.
|
||||
* @param callback The worker.
|
||||
* @param write Whether this is a write operation.
|
||||
* @return Whether the operation finished with a commit.
|
||||
*/
|
||||
public boolean perform(HibernateTxnCallback callback)
|
||||
public void perform(HibernateTxnCallback callback, boolean write)
|
||||
{
|
||||
Session sess = null;
|
||||
Transaction txn = null;
|
||||
try
|
||||
while (true)
|
||||
{
|
||||
sess = fSessionFactory.openSession();
|
||||
txn = sess.beginTransaction();
|
||||
callback.perform(sess);
|
||||
txn.commit();
|
||||
return true;
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
t.printStackTrace(System.err);
|
||||
if (txn != null)
|
||||
try
|
||||
{
|
||||
try
|
||||
/*
|
||||
if (write)
|
||||
{
|
||||
txn.rollback();
|
||||
fLock.writeLock().lock();
|
||||
}
|
||||
catch (HibernateException he)
|
||||
else
|
||||
{
|
||||
// Do nothing.
|
||||
fLock.readLock().lock();
|
||||
}
|
||||
*/
|
||||
sess = fSessionFactory.openSession();
|
||||
txn = sess.beginTransaction();
|
||||
callback.perform(sess);
|
||||
txn.commit();
|
||||
return;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (sess != null)
|
||||
catch (Throwable t)
|
||||
{
|
||||
try
|
||||
// TODO Add appropriate logging.
|
||||
if (txn != null)
|
||||
{
|
||||
sess.close();
|
||||
try
|
||||
{
|
||||
txn.rollback();
|
||||
}
|
||||
catch (HibernateException he)
|
||||
{
|
||||
// Do nothing.
|
||||
}
|
||||
// If we've lost a race or we've deadlocked, retry.
|
||||
if (t instanceof StaleStateException ||
|
||||
t instanceof GenericJDBCException)
|
||||
{
|
||||
if (t instanceof StaleStateException)
|
||||
{
|
||||
System.err.println("Lost Race");
|
||||
continue;
|
||||
}
|
||||
System.err.println("Deadlock");
|
||||
try
|
||||
{
|
||||
long interval;
|
||||
synchronized (fRandom)
|
||||
{
|
||||
interval = fRandom.nextInt(1000);
|
||||
}
|
||||
Thread.sleep(interval);
|
||||
}
|
||||
catch (InterruptedException ie)
|
||||
{
|
||||
// Do nothing.
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
catch (HibernateException he)
|
||||
if (t instanceof AVMException)
|
||||
{
|
||||
// Do nothing.
|
||||
throw (AVMException)t;
|
||||
}
|
||||
// TODO Crack t into more useful exception types.
|
||||
// Otherwise nothing we can do except throw.
|
||||
throw new AVMException("Unrecoverable error", t);
|
||||
}
|
||||
finally
|
||||
{
|
||||
/*
|
||||
if (write)
|
||||
{
|
||||
fLock.writeLock().unlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
fLock.readLock().unlock();
|
||||
}
|
||||
*/
|
||||
if (sess != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
sess.close();
|
||||
}
|
||||
catch (HibernateException he)
|
||||
{
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user