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:
Britt Park
2006-06-16 16:59:15 +00:00
parent 6b52660c8a
commit 936f6d7021
57 changed files with 9563 additions and 3827 deletions

View File

@@ -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>

View File

@@ -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.
}
}
}
}