diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml index eec7adbff8..1035e24cf7 100644 --- a/config/alfresco/core-services-context.xml +++ b/config/alfresco/core-services-context.xml @@ -318,9 +318,27 @@ - - - + + + org.alfresco.repo.version.common.counter.VersionCounterDaoService + + + + + + + + + + + + + + + + + ${server.transaction.mode.default}, PROPAGATION_REQUIRES_NEW + diff --git a/source/java/org/alfresco/repo/domain/hibernate/VersionCountImpl.java b/source/java/org/alfresco/repo/domain/hibernate/VersionCountImpl.java index af59bb8656..e091f101e8 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/VersionCountImpl.java +++ b/source/java/org/alfresco/repo/domain/hibernate/VersionCountImpl.java @@ -19,7 +19,6 @@ package org.alfresco.repo.domain.hibernate; import org.alfresco.repo.domain.Node; import org.alfresco.repo.domain.StoreKey; import org.alfresco.repo.domain.VersionCount; -import org.alfresco.service.cmr.repository.StoreRef; /** * Hibernate-specific implementation of the domain entity versioncounter. @@ -29,8 +28,9 @@ import org.alfresco.service.cmr.repository.StoreRef; public class VersionCountImpl implements VersionCount { private StoreKey key; + @SuppressWarnings("unused") + private long version; // used by Hibernate for concurrency private int versionCount; - private transient StoreRef storeRef; public VersionCountImpl() { @@ -78,9 +78,9 @@ public class VersionCountImpl implements VersionCount return key; } - public synchronized void setKey(StoreKey key) { + public synchronized void setKey(StoreKey key) + { this.key = key; - this.storeRef = null; } /** @@ -93,7 +93,9 @@ public class VersionCountImpl implements VersionCount public int incrementVersionCount() { - return ++versionCount; + int versionCount = getVersionCount() + 1; + setVersionCount(versionCount); + return versionCount; } /** diff --git a/source/java/org/alfresco/repo/node/index/NodeIndexer.java b/source/java/org/alfresco/repo/node/index/NodeIndexer.java index f853e709d6..aa7adb40f0 100644 --- a/source/java/org/alfresco/repo/node/index/NodeIndexer.java +++ b/source/java/org/alfresco/repo/node/index/NodeIndexer.java @@ -16,14 +16,12 @@ */ package org.alfresco.repo.node.index; -import org.alfresco.model.ContentModel; import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.search.Indexer; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; @@ -34,8 +32,7 @@ import org.alfresco.service.namespace.QName; * @author Derek Hulley */ public class NodeIndexer - implements NodeServicePolicies.BeforeCreateStorePolicy, - NodeServicePolicies.OnCreateNodePolicy, + implements NodeServicePolicies.OnCreateNodePolicy, NodeServicePolicies.OnUpdateNodePolicy, NodeServicePolicies.OnDeleteNodePolicy, NodeServicePolicies.OnCreateChildAssociationPolicy, @@ -67,10 +64,6 @@ public class NodeIndexer */ public void init() { - policyComponent.bindClassBehaviour( - QName.createQName(NamespaceService.ALFRESCO_URI, "beforeCreateStore"), - ContentModel.TYPE_STOREROOT, - new JavaBehaviour(this, "beforeCreateStore")); policyComponent.bindClassBehaviour( QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateNode"), this, @@ -93,11 +86,6 @@ public class NodeIndexer new JavaBehaviour(this, "onDeleteChildAssociation")); } - public void beforeCreateStore(QName nodeTypeQName, StoreRef storeRef) - { - // indexer can perform some cleanup here, if required - } - public void onCreateNode(ChildAssociationRef childAssocRef) { indexer.createNode(childAssocRef); diff --git a/source/java/org/alfresco/repo/security/permissions/impl/hibernate/Permission.hbm.xml b/source/java/org/alfresco/repo/security/permissions/impl/hibernate/Permission.hbm.xml index efd5ab818d..17c1182802 100644 --- a/source/java/org/alfresco/repo/security/permissions/impl/hibernate/Permission.hbm.xml +++ b/source/java/org/alfresco/repo/security/permissions/impl/hibernate/Permission.hbm.xml @@ -8,7 +8,7 @@ versionWork = new TransactionWork() + { + public Object doWork() throws Exception + { + // wait for all other threads to enter into their transactions + synchronized(beginWait) + { + waitCount++; + beginWait.wait(10000L); + } + + int startVersion = counter.currentVersionNumber(storeRef1); + // increment it + int incrementedVersion = counter.nextVersionNumber(storeRef1); + assertTrue("Version number was not incremented", incrementedVersion > startVersion); + + // wait for all other threads to have finished their increments + synchronized(endWait) + { + waitCount++; + endWait.wait(10000L); + } + + return null; + } + }; + try + { + TransactionUtil.executeInNonPropagatingUserTransaction(transactionService, versionWork, false); + error = null; + } + catch (Throwable e) + { + error = e; + e.printStackTrace(); + } + } + } } diff --git a/source/java/org/alfresco/repo/version/common/counter/hibernate/HibernateVersionCounterDaoServiceImpl.java b/source/java/org/alfresco/repo/version/common/counter/hibernate/HibernateVersionCounterDaoServiceImpl.java index 159dd409c0..f9f7781599 100644 --- a/source/java/org/alfresco/repo/version/common/counter/hibernate/HibernateVersionCounterDaoServiceImpl.java +++ b/source/java/org/alfresco/repo/version/common/counter/hibernate/HibernateVersionCounterDaoServiceImpl.java @@ -19,8 +19,14 @@ package org.alfresco.repo.version.common.counter.hibernate; import org.alfresco.repo.domain.StoreKey; import org.alfresco.repo.domain.VersionCount; import org.alfresco.repo.domain.hibernate.VersionCountImpl; +import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.version.common.counter.VersionCounterDaoService; import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.hibernate.LockMode; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; /** @@ -33,28 +39,93 @@ import org.springframework.orm.hibernate3.support.HibernateDaoSupport; * * @author Derek Hulley */ -public class HibernateVersionCounterDaoServiceImpl extends HibernateDaoSupport implements VersionCounterDaoService +public class HibernateVersionCounterDaoServiceImpl + extends HibernateDaoSupport + implements VersionCounterDaoService, NodeServicePolicies.BeforeCreateStorePolicy { + private PolicyComponent policyComponent; + /** - * Retrieves or creates a version counter + * @param policyComponent the component to register behaviour + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + /** + * Bind to receive notifications of store creations + */ + public void init() + { + policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "beforeCreateStore"), + this, + new JavaBehaviour(this, "beforeCreateStore")); + } + + /** + * Create a version counter for the store + * @param nodeTypeQName + * @param storeRef + */ + public void beforeCreateStore(QName nodeTypeQName, StoreRef storeRef) + { + final StoreKey storeKey = new StoreKey(storeRef.getProtocol(), storeRef.getIdentifier()); + VersionCount versionCount = (VersionCount) getHibernateTemplate().get(VersionCountImpl.class, storeKey); + if (versionCount != null) + { + // already exists + return; + } + versionCount = new VersionCountImpl(); + versionCount.setKey(storeKey); + getHibernateTemplate().save(versionCount); + } + + /** + * Retrieves or creates a version counter. This locks the counter against updates for the + * current transaction. * - * @param storeKey + * @param storeKey the primary key of the counter * @return Returns a current or new version counter */ private VersionCount getVersionCounter(StoreRef storeRef) { - StoreKey storeKey = new StoreKey(storeRef.getProtocol(), storeRef.getIdentifier()); - // get the version counter - VersionCount versionCounter = (VersionCount) getHibernateTemplate().get(VersionCountImpl.class, storeKey); + final StoreKey storeKey = new StoreKey(storeRef.getProtocol(), storeRef.getIdentifier()); + // check if it exists - if (versionCounter == null) + VersionCount versionCount = (VersionCount) getHibernateTemplate().get( + VersionCountImpl.class, + storeKey, + LockMode.UPGRADE); + if (versionCount == null) { - // create a new one - versionCounter = new VersionCountImpl(); - versionCounter.setKey(storeKey); - getHibernateTemplate().save(versionCounter); + // This could fail on some databases with concurrent adds. But it is only those databases + // that don't lock the index against an addition of the row, and then it will only fail once. + versionCount = new VersionCountImpl(); + versionCount.setKey(storeKey); + getHibernateTemplate().save(versionCount); + // debug + if (logger.isDebugEnabled()) + { + logger.debug("Created version counter: \n" + + " Thread: " + Thread.currentThread().getName() + "\n" + + " Version count: " + versionCount.getVersionCount()); + } } - return versionCounter; + else + { + // debug + if (logger.isDebugEnabled()) + { + logger.debug("Got version counter: \n" + + " Thread: " + Thread.currentThread().getName() + "\n" + + " Version count: " + versionCount.getVersionCount()); + } + } + // done + return versionCount; } /** @@ -63,12 +134,21 @@ public class HibernateVersionCounterDaoServiceImpl extends HibernateDaoSupport i * @param storeRef the version store id * @return the next version number */ - public synchronized int nextVersionNumber(StoreRef storeRef) + public int nextVersionNumber(StoreRef storeRef) { // get the version counter - VersionCount versionCounter = getVersionCounter(storeRef); + VersionCount versionCount = getVersionCounter(storeRef); // get an incremented count - return versionCounter.incrementVersionCount(); + int nextCount = versionCount.incrementVersionCount(); + + // done + if (logger.isDebugEnabled()) + { + logger.debug("Incremented version count: \n" + + " Thread: " + Thread.currentThread().getName() + "\n" + + " New version count: " + versionCount.getVersionCount()); + } + return nextCount; } /** @@ -77,7 +157,7 @@ public class HibernateVersionCounterDaoServiceImpl extends HibernateDaoSupport i * @param storeRef the store reference * @return the current version number, zero if no version yet allocated. */ - public synchronized int currentVersionNumber(StoreRef storeRef) + public int currentVersionNumber(StoreRef storeRef) { // get the version counter VersionCount versionCounter = getVersionCounter(storeRef);