diff --git a/config/alfresco/cache-context.xml b/config/alfresco/cache-context.xml index d0cb9c2586..bda3775e73 100644 --- a/config/alfresco/cache-context.xml +++ b/config/alfresco/cache-context.xml @@ -26,10 +26,10 @@ - + - + diff --git a/source/java/org/alfresco/repo/cache/AbstractAsynchronouslyRefreshedCache.java b/source/java/org/alfresco/repo/cache/AbstractAsynchronouslyRefreshedCache.java deleted file mode 100644 index 9fd9573a7d..0000000000 --- a/source/java/org/alfresco/repo/cache/AbstractAsynchronouslyRefreshedCache.java +++ /dev/null @@ -1,730 +0,0 @@ -/* - * Copyright (C) 2005-2013 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ -package org.alfresco.repo.cache; - -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.locks.ReentrantReadWriteLock; - -import org.alfresco.repo.tenant.TenantService; -import org.alfresco.repo.transaction.AlfrescoTransactionSupport; -import org.alfresco.repo.transaction.TransactionListener; -import org.alfresco.util.PropertyCheck; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.beans.factory.BeanNameAware; -import org.springframework.beans.factory.InitializingBean; - -/** - * The base implementation for an asynchronously refreshed cache. Currently supports one value per tenant. Implementors - * just need to provide buildCache(String tenanaId) - * - * @author Andy - * @since 4.1.3 - */ -public abstract class AbstractAsynchronouslyRefreshedCache implements AsynchronouslyRefreshedCache, RefreshableCacheListener, Callable, BeanNameAware, - InitializingBean, TransactionListener -{ - private static final String RESOURCE_KEY_TXN_DATA = "AbstractAsynchronouslyRefreshedCache.TxnData"; - - private static Log logger = LogFactory.getLog(AbstractAsynchronouslyRefreshedCache.class); - - private enum RefreshState - { - IDLE, WAITING, RUNNING, DONE - }; - - private ThreadPoolExecutor threadPoolExecutor; - private AsynchronouslyRefreshedCacheRegistry registry; - private TenantService tenantService; - - // State - - private List listeners = new LinkedList(); - private final ReentrantReadWriteLock liveLock = new ReentrantReadWriteLock(); - private final ReentrantReadWriteLock refreshLock = new ReentrantReadWriteLock(); - private final ReentrantReadWriteLock runLock = new ReentrantReadWriteLock(); - private HashMap live = new HashMap(); - private LinkedHashSet refreshQueue = new LinkedHashSet(); - private String cacheId; - private RefreshState refreshState = RefreshState.IDLE; - private String resourceKeyTxnData; - - @Override - public void register(RefreshableCacheListener listener) - { - listeners.add(listener); - } - - /** - * @param threadPool - * the threadPool to set - */ - public void setThreadPoolExecutor(ThreadPoolExecutor threadPoolExecutor) - { - this.threadPoolExecutor = threadPoolExecutor; - } - - /** - * @param registry - * the registry to set - */ - public void setRegistry(AsynchronouslyRefreshedCacheRegistry registry) - { - this.registry = registry; - } - - /** - * @param tenantService - * the tenantService to set - */ - public void setTenantService(TenantService tenantService) - { - this.tenantService = tenantService; - } - - public void init() - { - registry.register(this); - } - - @Override - public T get() - { - String tenantId = tenantService.getCurrentUserDomain(); - liveLock.readLock().lock(); - try - { - if (live.get(tenantId) != null) - { - if (logger.isDebugEnabled()) - { - logger.debug("get() from cache"); - } - return live.get(tenantId); - } - } - finally - { - liveLock.readLock().unlock(); - } - - if (logger.isDebugEnabled()) - { - logger.debug("get() miss, sechudling and waiting ..."); - } - - // There was nothing to return so we build and return - Refresh refresh = null; - refreshLock.writeLock().lock(); - try - { - // Is there anything we can wait for - for (Refresh existing : refreshQueue) - { - if (existing.getTenantId().equals(tenantId)) - { - if (logger.isDebugEnabled()) - { - logger.debug("get() found existing build to wait for ..."); - } - refresh = existing; - } - } - - if (refresh == null) - { - if (logger.isDebugEnabled()) - { - logger.debug("get() building from scratch"); - } - refresh = new Refresh(tenantId); - refreshQueue.add(refresh); - } - - } - finally - { - refreshLock.writeLock().unlock(); - } - submit(); - waitForBuild(refresh); - - return get(); - } - - public void forceInChangesForThisUncommittedTransaction() - { - String tenantId = tenantService.getCurrentUserDomain(); - if (logger.isDebugEnabled()) - { - logger.debug("Building cache for tenant " + tenantId + " ......"); - } - T cache = buildCache(tenantId); - if (logger.isDebugEnabled()) - { - logger.debug(".... cache built for tenant " + tenantId); - } - - liveLock.writeLock().lock(); - try - { - live.put(tenantId, cache); - } - finally - { - liveLock.writeLock().unlock(); - } - } - - protected void waitForBuild(Refresh refresh) - { - while (refresh.getState() != RefreshState.DONE) - { - synchronized (refresh) - { - try - { - refresh.wait(100); - } - catch (InterruptedException e) - { - } - } - } - } - - @Override - public void refresh() - { - String tenantId = tenantService.getCurrentUserDomain(); - if (logger.isDebugEnabled()) - { - logger.debug("Async cache refresh request: " + cacheId + " for tenant " + tenantId); - } - registry.broadcastEvent(new RefreshableCacheRefreshEvent(cacheId, tenantId), true); - } - - @Override - public void onRefreshableCacheEvent(RefreshableCacheEvent refreshableCacheEvent) - { - if (logger.isDebugEnabled()) - { - logger.debug("Async cache onRefreshableCacheEvent " + refreshableCacheEvent); - } - if (false == refreshableCacheEvent.getCacheId().equals(cacheId)) - { - return; - } - - // If in a transaction delay the refresh until after it commits - - if (AlfrescoTransactionSupport.getTransactionId() != null) - { - if (logger.isDebugEnabled()) - { - logger.debug("Async cache adding" + refreshableCacheEvent.getTenantId() + " to post commit list"); - } - TransactionData txData = getTransactionData(); - txData.tenantIds.add(refreshableCacheEvent.getTenantId()); - } - else - { - LinkedHashSet tenantIds = new LinkedHashSet(); - tenantIds.add(refreshableCacheEvent.getTenantId()); - queueRefreshAndSubmit(tenantIds); - } - } - - /** - * To be used in a transaction only. - */ - private TransactionData getTransactionData() - { - TransactionData data = (TransactionData) AlfrescoTransactionSupport.getResource(resourceKeyTxnData); - if (data == null) - { - data = new TransactionData(); - // create and initialize caches - data.tenantIds = new LinkedHashSet(); - - // ensure that we get the transaction callbacks as we have bound the unique - // transactional caches to a common manager - AlfrescoTransactionSupport.bindListener(this); - AlfrescoTransactionSupport.bindResource(resourceKeyTxnData, data); - } - return data; - } - - private void queueRefreshAndSubmit(LinkedHashSet tenantIds) - { - if((tenantIds == null) || (tenantIds.size() == 0)) - { - return; - } - refreshLock.writeLock().lock(); - try - { - for (String tenantId : tenantIds) - { - if (logger.isDebugEnabled()) - { - logger.debug("Async cache adding refresh to queue for "+tenantId); - } - refreshQueue.add(new Refresh(tenantId)); - } - } - finally - { - refreshLock.writeLock().unlock(); - } - submit(); - } - - @Override - public boolean isUpToDate() - { - String tenantId = tenantService.getCurrentUserDomain(); - refreshLock.readLock().lock(); - try - { - for(Refresh refresh : refreshQueue) - { - if(refresh.getTenantId().equals(tenantId)) - { - return false; - } - } - if (AlfrescoTransactionSupport.getTransactionId() != null) - { - return (!getTransactionData().tenantIds.contains(tenantId)); - } - else - { - return true; - } - } - finally - { - refreshLock.readLock().unlock(); - } - } - - /** - * Must be run with runLock.writeLock - */ - private Refresh getNextRefresh() - { - if (runLock.writeLock().isHeldByCurrentThread()) - { - for (Refresh refresh : refreshQueue) - { - if (refresh.state == RefreshState.WAITING) - { - return refresh; - } - } - return null; - } - else - { - throw new IllegalStateException("Method should not be called without holding the write lock"); - } - - } - - /** - * Must be run with runLock.writeLock - */ - private int countWaiting() - { - int count = 0; - if (runLock.writeLock().isHeldByCurrentThread()) - { - refreshLock.readLock().lock(); - try - { - for (Refresh refresh : refreshQueue) - { - if (refresh.state == RefreshState.WAITING) - { - count++; - } - } - return count; - } - finally - { - refreshLock.readLock().unlock(); - } - } - else - { - throw new IllegalStateException("Method should not be called without holding the write lock"); - } - - } - - private void submit() - { - runLock.writeLock().lock(); - try - { - if (refreshState == RefreshState.IDLE) - { - if (logger.isDebugEnabled()) - { - logger.debug("submit() scheduling job"); - } - threadPoolExecutor.submit(this); - refreshState = RefreshState.WAITING; - } - } - finally - { - runLock.writeLock().unlock(); - } - } - - @Override - public Void call() - { - try - { - doCall(); - return null; - } - catch (Exception e) - { - logger.error("Cache update failed (" + this.getCacheId() + ").", e); - runLock.writeLock().lock(); - try - { - threadPoolExecutor.submit(this); - refreshState = RefreshState.WAITING; - } - finally - { - runLock.writeLock().unlock(); - } - return null; - } - } - - private void doCall() throws Exception - { - Refresh refresh = setUpRefresh(); - if (refresh == null) - { - return; - } - - if (logger.isDebugEnabled()) - { - logger.debug("Building cache for tenant" + refresh.getTenantId()); - } - - try - { - doRefresh(refresh); - } - catch (Exception e) - { - refresh.setState(RefreshState.WAITING); - throw e; - } - } - - private void doRefresh(Refresh refresh) - { - if (logger.isDebugEnabled()) - { - logger.debug("Building cache for tenant" + refresh.getTenantId() + " ......"); - } - T cache = buildCache(refresh.getTenantId()); - if (logger.isDebugEnabled()) - { - logger.debug(".... cache built for tenant" + refresh.getTenantId()); - } - - liveLock.writeLock().lock(); - try - { - live.put(refresh.getTenantId(), cache); - } - finally - { - liveLock.writeLock().unlock(); - } - - if (logger.isDebugEnabled()) - { - logger.debug("Cache entry updated for tenant" + refresh.getTenantId()); - } - - broadcastEvent(new RefreshableCacheRefreshedEvent(cacheId, refresh.tenantId)); - - runLock.writeLock().lock(); - try - { - refreshLock.writeLock().lock(); - try - { - if (countWaiting() > 0) - { - if (logger.isDebugEnabled()) - { - logger.debug("Rescheduling ... more work"); - } - threadPoolExecutor.submit(this); - refreshState = RefreshState.WAITING; - } - else - { - if (logger.isDebugEnabled()) - { - logger.debug("Nothing to do .... going idle"); - } - refreshState = RefreshState.IDLE; - } - refresh.setState(RefreshState.DONE); - refreshQueue.remove(refresh); - } - finally - { - refreshLock.writeLock().unlock(); - } - } - finally - { - runLock.writeLock().unlock(); - } - } - - private Refresh setUpRefresh() throws Exception - { - Refresh refresh = null; - runLock.writeLock().lock(); - try - { - if (refreshState == RefreshState.WAITING) - { - refreshLock.writeLock().lock(); - try - { - refresh = getNextRefresh(); - if (refresh != null) - { - refreshState = RefreshState.RUNNING; - refresh.setState(RefreshState.RUNNING); - return refresh; - } - else - { - refreshState = RefreshState.IDLE; - return null; - } - } - finally - { - refreshLock.writeLock().unlock(); - } - } - else - { - return null; - } - } - catch (Exception e) - { - if (refresh != null) - { - refresh.setState(RefreshState.WAITING); - } - throw e; - } - finally - { - runLock.writeLock().unlock(); - } - - } - - @Override - public void setBeanName(String name) - { - cacheId = name; - - } - - @Override - public String getCacheId() - { - return cacheId; - } - - /** - * Build the cache entry for the specific tenant. - * This method is called in a thread-safe manner i.e. it is only ever called by a single - * thread. - */ - protected abstract T buildCache(String tenantId); - - private static class Refresh - { - private String tenantId; - - private volatile RefreshState state = RefreshState.WAITING; - - Refresh(String tenantId) - { - this.tenantId = tenantId; - } - - /** - * @return the tenantId - */ - public String getTenantId() - { - return tenantId; - } - - /** - * @return the state - */ - public RefreshState getState() - { - return state; - } - - /** - * @param state - * the state to set - */ - public void setState(RefreshState state) - { - this.state = state; - } - - @Override - public int hashCode() - { - // The bucked is determined by the tenantId alone - we are going to change the state - final int prime = 31; - int result = 1; - result = prime * result + ((tenantId == null) ? 0 : tenantId.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Refresh other = (Refresh) obj; - if (state != other.state) - return false; - if (tenantId == null) - { - if (other.tenantId != null) - return false; - } - else if (!tenantId.equals(other.tenantId)) - return false; - return true; - } - - @Override - public String toString() - { - return "Refresh [tenantId=" + tenantId + ", state=" + state + ", hashCode()=" + hashCode() + "]"; - } - - } - - @Override - public void afterPropertiesSet() throws Exception - { - PropertyCheck.mandatory(this, "threadPoolExecutor", threadPoolExecutor); - PropertyCheck.mandatory(this, "tenantService", tenantService); - PropertyCheck.mandatory(this, "registry", registry); - registry.register(this); - - resourceKeyTxnData = RESOURCE_KEY_TXN_DATA + "." + cacheId; - - } - - public void broadcastEvent(RefreshableCacheEvent event) - { - if (logger.isDebugEnabled()) - { - logger.debug("Notifying cache listeners for " + getCacheId() + " " + event); - } - // If the system is up and running, broadcast the event immediately - for (RefreshableCacheListener listener : this.listeners) - { - listener.onRefreshableCacheEvent(event); - } - - } - - @Override - public void flush() - { - // Nothing - } - - @Override - public void beforeCommit(boolean readOnly) - { - // Nothing - } - - @Override - public void beforeCompletion() - { - // Nothing - } - - @Override - public void afterCommit() - { - TransactionData txnData = getTransactionData(); - queueRefreshAndSubmit(txnData.tenantIds); - } - - @Override - public void afterRollback() - { - // Nothing - } - - private static class TransactionData - { - LinkedHashSet tenantIds; - } -} diff --git a/source/java/org/alfresco/repo/cache/AbstractMTAsynchronouslyRefreshedCache.java b/source/java/org/alfresco/repo/cache/AbstractMTAsynchronouslyRefreshedCache.java new file mode 100644 index 0000000000..3201fe1351 --- /dev/null +++ b/source/java/org/alfresco/repo/cache/AbstractMTAsynchronouslyRefreshedCache.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.cache; + +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import org.alfresco.repo.tenant.TenantService; +import org.alfresco.repo.transaction.AlfrescoTransactionSupport; +import org.alfresco.repo.transaction.TransactionListener; +import org.alfresco.util.PropertyCheck; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.BeanNameAware; +import org.springframework.beans.factory.InitializingBean; + +/** + * The base implementation for Multi-tenant asynchronously refreshed cache. Currently supports one value per tenant. + * + * Implementors just need to provide buildCache(String tennantId) + * + * @author Andy + * @since 4.1.3 + */ +public abstract class AbstractMTAsynchronouslyRefreshedCache + extends org.alfresco.util.cache.AbstractAsynchronouslyRefreshedCache + implements AsynchronouslyRefreshedCache, InitializingBean +{ + + private static Log logger = LogFactory.getLog(AbstractMTAsynchronouslyRefreshedCache.class); + + private TenantService tenantService; + + /** + * @param tenantService + * the tenantService to set + */ + public void setTenantService(TenantService tenantService) + { + this.tenantService = tenantService; + } + + + @Override + public T get() + { + String tenantId = tenantService.getCurrentUserDomain(); + return get(tenantId); + } + + public void forceInChangesForThisUncommittedTransaction() + { + String tenantId = tenantService.getCurrentUserDomain(); + forceInChangesForThisUncommittedTransaction(tenantId); + } + + @Override + public void refresh() + { + String tenantId = tenantService.getCurrentUserDomain(); + refresh(tenantId); + } + + + @Override + public boolean isUpToDate() + { + String tenantId = tenantService.getCurrentUserDomain(); + return isUpToDate(tenantId); + } + + /** + * Build the cache entry for the specific tenant. + * This method is called in a thread-safe manner i.e. it is only ever called by a single + * thread. + */ + protected abstract T buildCache(String tenantId); + + + @Override + public void afterPropertiesSet() throws Exception + { + PropertyCheck.mandatory(this, "tenantService", tenantService); + super.afterPropertiesSet(); + } +} diff --git a/source/java/org/alfresco/repo/cache/DefaultAsynchronouslyRefreshedCacheRegistry.java b/source/java/org/alfresco/repo/cache/DefaultAsynchronouslyRefreshedCacheRegistry.java deleted file mode 100644 index 47312b56d3..0000000000 --- a/source/java/org/alfresco/repo/cache/DefaultAsynchronouslyRefreshedCacheRegistry.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2005-2013 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ -package org.alfresco.repo.cache; - -import java.util.LinkedList; -import java.util.List; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * Base registry implementation - * - * @author Andy - */ -public class DefaultAsynchronouslyRefreshedCacheRegistry implements AsynchronouslyRefreshedCacheRegistry -{ - private static Log logger = LogFactory.getLog(DefaultAsynchronouslyRefreshedCacheRegistry.class); - - private List listeners = new LinkedList(); - - @Override - public void register(RefreshableCacheListener listener) - { - if(logger.isDebugEnabled()) - { - logger.debug("Listener added for " + listener.getCacheId()); - } - listeners.add(listener); - } - - public void broadcastEvent(RefreshableCacheEvent event, boolean toAll) - { - // If the system is up and running, broadcast the event immediately - for (RefreshableCacheListener listener : this.listeners) - { - if (toAll) - { - if(logger.isDebugEnabled()) - { - logger.debug("Delivering event (" + event + ") to listener (" + listener + ")."); - } - listener.onRefreshableCacheEvent(event); - } - else - { - if (listener.getCacheId().equals(event.getCacheId())) - { - if(logger.isDebugEnabled()) - { - logger.debug("Delivering event (" + event + ") to listener (" + listener + ")."); - } - listener.onRefreshableCacheEvent(event); - } - } - } - } -} diff --git a/source/java/org/alfresco/repo/cache/RefreshableCache.java b/source/java/org/alfresco/repo/cache/RefreshableCache.java index 8ece35bf93..084d796011 100644 --- a/source/java/org/alfresco/repo/cache/RefreshableCache.java +++ b/source/java/org/alfresco/repo/cache/RefreshableCache.java @@ -40,12 +40,12 @@ public interface RefreshableCache */ public void refresh(); - /** - * Register to be informed when the cache is updated in the background. - * - * Note: it is up to the implementation to provide any transactional wrapping. - * Transactional wrapping is not required to invalidate a shared cache entry directly via a transactional cache - * @param listener - */ - void register(RefreshableCacheListener listener); +// /** +// * Register to be informed when the cache is updated in the background. +// * +// * Note: it is up to the implementation to provide any transactional wrapping. +// * Transactional wrapping is not required to invalidate a shared cache entry directly via a transactional cache +// * @param listener +// */ +// void register(RefreshableCacheListener listener); } diff --git a/source/java/org/alfresco/repo/cache/RefreshableCacheListener.java b/source/java/org/alfresco/repo/cache/RefreshableCacheListener.java index 8f6c0f61e9..4141a4e446 100644 --- a/source/java/org/alfresco/repo/cache/RefreshableCacheListener.java +++ b/source/java/org/alfresco/repo/cache/RefreshableCacheListener.java @@ -23,19 +23,7 @@ package org.alfresco.repo.cache; * * @author Andy */ -public interface RefreshableCacheListener +public interface RefreshableCacheListener extends org.alfresco.util.cache.RefreshableCacheListener { - /** - * Callback made when a cache refresh occurs - * - * @param the cache event - */ - public void onRefreshableCacheEvent(RefreshableCacheEvent refreshableCacheEvent); - - /** - * Cache id so broadcast can be constrained to matching caches - * - * @return the cache ID - */ - public String getCacheId(); + } diff --git a/source/java/org/alfresco/repo/config/ConfigDataCache.java b/source/java/org/alfresco/repo/config/ConfigDataCache.java index b7614db29d..1f9f4d21d7 100644 --- a/source/java/org/alfresco/repo/config/ConfigDataCache.java +++ b/source/java/org/alfresco/repo/config/ConfigDataCache.java @@ -22,7 +22,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; -import org.alfresco.repo.cache.AbstractAsynchronouslyRefreshedCache; +import org.alfresco.repo.cache.AbstractMTAsynchronouslyRefreshedCache; import org.alfresco.repo.config.ConfigDataCache.ConfigData; import org.alfresco.repo.config.xml.RepoXMLConfigService; import org.apache.commons.logging.Log; @@ -50,7 +50,7 @@ import com.sun.star.uno.RuntimeException; * @author Andy Hind * @since 4.1.5 */ -public class ConfigDataCache extends AbstractAsynchronouslyRefreshedCache +public class ConfigDataCache extends AbstractMTAsynchronouslyRefreshedCache { private static Log logger = LogFactory.getLog(ConfigDataCache.class); diff --git a/source/java/org/alfresco/repo/node/integrity/IntegrityChecker.java b/source/java/org/alfresco/repo/node/integrity/IntegrityChecker.java index ba05e04c9e..359d51a3f1 100644 --- a/source/java/org/alfresco/repo/node/integrity/IntegrityChecker.java +++ b/source/java/org/alfresco/repo/node/integrity/IntegrityChecker.java @@ -43,6 +43,8 @@ import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.util.PropertyCheck; +import org.alfresco.util.transaction.TransactionListener; +import org.alfresco.util.transaction.TransactionSupportUtil; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -86,7 +88,8 @@ public class IntegrityChecker NodeServicePolicies.OnCreateChildAssociationPolicy, NodeServicePolicies.OnDeleteChildAssociationPolicy, NodeServicePolicies.OnCreateAssociationPolicy, - NodeServicePolicies.OnDeleteAssociationPolicy + NodeServicePolicies.OnDeleteAssociationPolicy, + TransactionListener { private static Log logger = LogFactory.getLog(IntegrityChecker.class); @@ -113,7 +116,7 @@ public class IntegrityChecker */ public static void setWarnInTransaction() { - AlfrescoTransactionSupport.bindResource(KEY_WARN_IN_TRANSACTION, Boolean.TRUE); + TransactionSupportUtil.bindResource(KEY_WARN_IN_TRANSACTION, Boolean.TRUE); } /** @@ -124,7 +127,7 @@ public class IntegrityChecker */ public static boolean isWarnInTransaction() { - Boolean warnInTransaction = (Boolean) AlfrescoTransactionSupport.getResource(KEY_WARN_IN_TRANSACTION); + Boolean warnInTransaction = (Boolean) TransactionSupportUtil.getResource(KEY_WARN_IN_TRANSACTION); if (warnInTransaction == null || warnInTransaction == Boolean.FALSE) { return false; @@ -756,4 +759,28 @@ public class IntegrityChecker // done return allIntegrityResults; } + + @Override + public void beforeCommit(boolean readOnly) + { + checkIntegrity(); + } + + @Override + public void beforeCompletion() + { + // NO-OP + } + + @Override + public void afterCommit() + { + // NO-OP + } + + @Override + public void afterRollback() + { + // NO-OP + } } diff --git a/source/java/org/alfresco/repo/security/authority/AuthorityBridgeTableAsynchronouslyRefreshedCache.java b/source/java/org/alfresco/repo/security/authority/AuthorityBridgeTableAsynchronouslyRefreshedCache.java index a19c3b92c1..86025b8c61 100644 --- a/source/java/org/alfresco/repo/security/authority/AuthorityBridgeTableAsynchronouslyRefreshedCache.java +++ b/source/java/org/alfresco/repo/security/authority/AuthorityBridgeTableAsynchronouslyRefreshedCache.java @@ -20,7 +20,7 @@ package org.alfresco.repo.security.authority; import java.util.List; -import org.alfresco.repo.cache.AbstractAsynchronouslyRefreshedCache; +import org.alfresco.repo.cache.AbstractMTAsynchronouslyRefreshedCache; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.tenant.TenantAdminService; @@ -34,7 +34,7 @@ import org.springframework.beans.factory.InitializingBean; * @author Andy * @since 4.1.3 */ -public class AuthorityBridgeTableAsynchronouslyRefreshedCache extends AbstractAsynchronouslyRefreshedCache> implements InitializingBean +public class AuthorityBridgeTableAsynchronouslyRefreshedCache extends AbstractMTAsynchronouslyRefreshedCache> implements InitializingBean { private AuthorityBridgeDAO authorityBridgeDAO; private RetryingTransactionHelper retryingTransactionHelper; diff --git a/source/java/org/alfresco/repo/security/authority/AuthorityDAOImpl.java b/source/java/org/alfresco/repo/security/authority/AuthorityDAOImpl.java index 626d3b7b68..3b228fc2c6 100644 --- a/source/java/org/alfresco/repo/security/authority/AuthorityDAOImpl.java +++ b/source/java/org/alfresco/repo/security/authority/AuthorityDAOImpl.java @@ -42,8 +42,8 @@ import org.alfresco.query.CannedQueryResults; import org.alfresco.query.PagingRequest; import org.alfresco.query.PagingResults; import org.alfresco.repo.cache.AsynchronouslyRefreshedCache; -import org.alfresco.repo.cache.RefreshableCacheEvent; -import org.alfresco.repo.cache.RefreshableCacheListener; +import org.alfresco.util.cache.RefreshableCacheEvent; +import org.alfresco.util.cache.RefreshableCacheListener; import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.cache.TransactionalCache; import org.alfresco.repo.domain.permissions.AclDAO; @@ -121,7 +121,7 @@ public class AuthorityDAOImpl implements AuthorityDAO, NodeServicePolicies.Befor private SimpleCache> userAuthorityCache; private SimpleCache, List> zoneAuthorityCache; private SimpleCache, List>> childAuthorityCache; - private AsynchronouslyRefreshedCache> authorityBridgeTableCache; + private AuthorityBridgeTableAsynchronouslyRefreshedCache authorityBridgeTableCache; private SimpleCache singletonCache; // eg. for system container nodeRefs (authorityContainer and zoneContainer) private final String KEY_SYSTEMCONTAINER_NODEREF = "key.systemcontainer.noderef"; /** Limit the number of copies of authority names floating about by keeping them in a pool **/ diff --git a/source/java/org/alfresco/repo/transaction/AlfrescoTransactionSupport.java b/source/java/org/alfresco/repo/transaction/AlfrescoTransactionSupport.java index 460290c55a..ed4641cb0d 100644 --- a/source/java/org/alfresco/repo/transaction/AlfrescoTransactionSupport.java +++ b/source/java/org/alfresco/repo/transaction/AlfrescoTransactionSupport.java @@ -28,10 +28,13 @@ import java.util.Map; import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.jlan.client.AsynchRequest; import org.alfresco.repo.cache.TransactionalCache; import org.alfresco.repo.node.integrity.IntegrityChecker; import org.alfresco.repo.search.impl.lucene.LuceneIndexerAndSearcher; import org.alfresco.util.GUID; +import org.alfresco.util.transaction.TransactionListener; +import org.alfresco.util.transaction.TransactionSupportUtil; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.extensions.surf.util.ParameterCheck; @@ -41,13 +44,16 @@ import org.springframework.transaction.support.TransactionSynchronizationAdapter import org.springframework.transaction.support.TransactionSynchronizationManager; /** - * Helper class to manage transaction synchronization. This provides helpers to + * Repo Specific Helper class to manage transaction synchronization. This provides helpers to * ensure that the necessary TransactionSynchronization instances * are registered on behalf of the application code. * + * This class remains for backward API compatibility, + * @deprecated use the Core Project TransactionSupportUtil instead + * * @author Derek Hulley */ -public abstract class AlfrescoTransactionSupport +public abstract class AlfrescoTransactionSupport extends TransactionSupportUtil { /* * The registrations of services is very explicit on the interface. This @@ -56,6 +62,12 @@ public abstract class AlfrescoTransactionSupport * list of types of services that need registration, this is still * OK. */ + + private static int COMMIT_ORDER_NORMAL=0; + private static int COMMIT_ORDER_INTEGRITY=1; + private static int COMMIT_ORDER_LUCENE=2; + private static int COMMIT_ORDER_DAO=3; + private static int COMMIT_ORDER_CACHE=4; /** * The order of synchronization set to be 100 less than the Hibernate synchronization order @@ -63,74 +75,9 @@ public abstract class AlfrescoTransactionSupport public static final int SESSION_SYNCHRONIZATION_ORDER = SessionFactoryUtils.SESSION_SYNCHRONIZATION_ORDER - 100; - /** resource key to store the transaction synchronizer instance */ - private static final String RESOURCE_KEY_TXN_SYNCH = "txnSynch"; - /** resource binding during after-completion phase */ - private static final String RESOURCE_KEY_TXN_COMPLETING = "AlfrescoTransactionSupport.txnCompleting"; private static Log logger = LogFactory.getLog(AlfrescoTransactionSupport.class); - /** - * @return Returns the system time when the transaction started, or -1 if there is no current transaction. - */ - public static long getTransactionStartTime() - { - /* - * This method can be called outside of a transaction, so we can go direct to the synchronizations. - */ - TransactionSynchronizationImpl txnSynch = - (TransactionSynchronizationImpl) TransactionSynchronizationManager.getResource(RESOURCE_KEY_TXN_SYNCH); - if (txnSynch == null) - { - if (TransactionSynchronizationManager.isSynchronizationActive()) - { - // need to lazily register synchronizations - return registerSynchronizations().getTransactionStartTime(); - } - else - { - return -1; // not in a transaction - } - } - else - { - return txnSynch.getTransactionStartTime(); - } - } - - /** - * Get a unique identifier associated with each transaction of each thread. Null is returned if - * no transaction is currently active. - * - * @return Returns the transaction ID, or null if no transaction is present - */ - public static String getTransactionId() - { - /* - * Go direct to the synchronizations as we don't want to register a resource if one doesn't exist. - * This method is heavily used, so the simple Map lookup on the ThreadLocal is the fastest. - */ - - TransactionSynchronizationImpl txnSynch = - (TransactionSynchronizationImpl) TransactionSynchronizationManager.getResource(RESOURCE_KEY_TXN_SYNCH); - if (txnSynch == null) - { - if (TransactionSynchronizationManager.isSynchronizationActive()) - { - // need to lazily register synchronizations - return registerSynchronizations().getTransactionId(); - } - else - { - return null; // not in a transaction - } - } - else - { - return txnSynch.getTransactionId(); - } - } - /** * * @author Derek Hulley @@ -146,10 +93,7 @@ public abstract class AlfrescoTransactionSupport TXN_READ_WRITE } - public static boolean isActualTransactionActive() - { - return TransactionSynchronizationManager.isActualTransactionActive(); - } + /** * @return Returns the read-write state of the current transaction @@ -162,7 +106,7 @@ public abstract class AlfrescoTransactionSupport return TxnReadState.TXN_NONE; } // Find the read-write state of the txn - if (AlfrescoTransactionSupport.getResource(RESOURCE_KEY_TXN_COMPLETING) != null) + if (getResource(RESOURCE_KEY_TXN_COMPLETING) != null) { // Transaction is completing. For all intents and purposes, we are not in a transaction. return TxnReadState.TXN_NONE; @@ -212,93 +156,35 @@ public abstract class AlfrescoTransactionSupport * * @deprecated To be replaced by {@link DirtySessionMethodInterceptor} */ - public static boolean isDirty() + public static boolean isDirty() { - TransactionSynchronizationImpl synch = getSynchronization(); - - Set services = synch.getDaoServices(); - for (TransactionalDao service : services) - { - if (service.isDirty()) - { - return true; - } - } - + Set allListeners = getListeners(); + for(TransactionListener listener : allListeners) + { + if(listener instanceof TransactionalDao) + { + TransactionalDao service = (TransactionalDao)listener; + if (service.isDirty()) + { + return true; + } + + } + else if (listener instanceof DAOAdapter) + { + DAOAdapter adapter = (DAOAdapter)listener; + TransactionalDao service = adapter.getService(); + if (service.isDirty()) + { + return true; + } + } + + } + return false; } - /** - * Gets a resource associated with the current transaction, which must be active. - *

- * All necessary synchronization instances will be registered automatically, if required. - * - * - * @param key the thread resource map key - * @return Returns a thread resource of null if not present - * - * @see TransactionalResourceHelper for helper methods to create and bind common collection types - */ - @SuppressWarnings("unchecked") - public static R getResource(Object key) - { - // get the synchronization - TransactionSynchronizationImpl txnSynch = getSynchronization(); - // get the resource - Object resource = txnSynch.resources.get(key); - // done - if (logger.isDebugEnabled()) - { - logger.debug("Fetched resource: \n" + - " key: " + key + "\n" + - " resource: " + resource); - } - return (R) resource; - } - - /** - * Binds a resource to the current transaction, which must be active. - *

- * All necessary synchronization instances will be registered automatically, if required. - * - * @param key - * @param resource - */ - public static void bindResource(Object key, Object resource) - { - // get the synchronization - TransactionSynchronizationImpl txnSynch = getSynchronization(); - // bind the resource - txnSynch.resources.put(key, resource); - // done - if (logger.isDebugEnabled()) - { - logger.debug("Bound resource: \n" + - " key: " + key + "\n" + - " resource: " + resource); - } - } - - /** - * Unbinds a resource from the current transaction, which must be active. - *

- * All necessary synchronization instances will be registered automatically, if required. - * - * @param key - */ - public static void unbindResource(Object key) - { - // get the synchronization - TransactionSynchronizationImpl txnSynch = getSynchronization(); - // remove the resource - txnSynch.resources.remove(key); - // done - if (logger.isDebugEnabled()) - { - logger.debug("Unbound resource: \n" + - " key: " + key); - } - } /** * Method that registers a NodeDaoService against the transaction. @@ -312,11 +198,10 @@ public abstract class AlfrescoTransactionSupport */ public static void bindDaoService(TransactionalDao daoService) { - // get transaction-local synchronization - TransactionSynchronizationImpl synch = getSynchronization(); - // bind the service in - boolean bound = synch.getDaoServices().add(daoService); + DAOAdapter adapter = new DAOAdapter(daoService); + + boolean bound = bindListener(adapter, COMMIT_ORDER_DAO); // done if (logger.isDebugEnabled()) @@ -337,13 +222,10 @@ public abstract class AlfrescoTransactionSupport */ public static void bindIntegrityChecker(IntegrityChecker integrityChecker) { - // get transaction-local synchronization - TransactionSynchronizationImpl synch = getSynchronization(); - + // bind the service in - boolean bound = synch.getIntegrityCheckers().add(integrityChecker); + boolean bound = bindListener((TransactionListener) integrityChecker, COMMIT_ORDER_INTEGRITY); - // done if (logger.isDebugEnabled()) { logBoundService(integrityChecker, bound); @@ -365,12 +247,10 @@ public abstract class AlfrescoTransactionSupport */ public static void bindLucene(LuceneIndexerAndSearcher indexerAndSearcher) { - // get transaction-local synchronization - TransactionSynchronizationImpl synch = getSynchronization(); - - // bind the service in - boolean bound = synch.getLucenes().add(indexerAndSearcher); + LuceneIndexerAndSearcherAdapter adapter = new LuceneIndexerAndSearcherAdapter(indexerAndSearcher); + boolean bound = bindListener(adapter, COMMIT_ORDER_LUCENE); + // done if (logger.isDebugEnabled()) { @@ -379,13 +259,9 @@ public abstract class AlfrescoTransactionSupport } /** - * Method that registers a LuceneIndexerAndSearcherFactory against + * Method that registers a Listener against * the transaction. - *

- * Setting this will ensure that the pre- and post-commit operations perform - * the necessary cleanups against the LuceneIndexerAndSearcherFactory. - *

- * Although bound within a Set, it would still be better for the caller + *

will be better for the caller * to only bind once per transaction, if possible. * * @param indexerAndSearcher the Lucene indexer to perform transaction completion @@ -393,13 +269,21 @@ public abstract class AlfrescoTransactionSupport */ public static void bindListener(TransactionListener listener) { - // get transaction-local synchronization - TransactionSynchronizationImpl synch = getSynchronization(); - - // bind the service in - boolean bound = synch.addListener(listener); - - // done + boolean bound = false; + + if (listener instanceof IntegrityChecker) + { + bound = bindListener(listener, COMMIT_ORDER_INTEGRITY); + } + else if (listener instanceof TransactionalCache) + { + bound = bindListener(listener, COMMIT_ORDER_CACHE); + } + else + { + bound = bindListener(listener, COMMIT_ORDER_NORMAL); + } + if (logger.isDebugEnabled()) { logBoundService(listener, bound); @@ -437,426 +321,5 @@ public abstract class AlfrescoTransactionSupport { // No-op } - - /** - * Gets the current transaction synchronization instance, which contains the locally bound - * resources that are available to {@link #getResource(Object) retrieve} or - * {@link #bindResource(Object, Object) add to}. - *

- * This method also ensures that the transaction binding has been performed. - * - * @return Returns the common synchronization instance used - */ - private static TransactionSynchronizationImpl getSynchronization() - { - // ensure synchronizations - return registerSynchronizations(); - } - - /** - * Binds the Alfresco-specific to the transaction resources - * - * @return Returns the current or new synchronization implementation - */ - private static TransactionSynchronizationImpl registerSynchronizations() - { - /* - * No thread synchronization or locking required as the resources are all threadlocal - */ - if (!TransactionSynchronizationManager.isSynchronizationActive()) - { - Thread currentThread = Thread.currentThread(); - throw new AlfrescoRuntimeException("Transaction must be active and synchronization is required: " + currentThread); - } - TransactionSynchronizationImpl txnSynch = - (TransactionSynchronizationImpl) TransactionSynchronizationManager.getResource(RESOURCE_KEY_TXN_SYNCH); - if (txnSynch != null) - { - // synchronization already registered - return txnSynch; - } - // we need a unique ID for the transaction - String txnId = GUID.generate(); - // register the synchronization - txnSynch = new TransactionSynchronizationImpl(txnId); - TransactionSynchronizationManager.registerSynchronization(txnSynch); - // register the resource that will ensure we don't duplication the synchronization - TransactionSynchronizationManager.bindResource(RESOURCE_KEY_TXN_SYNCH, txnSynch); - // done - if (logger.isDebugEnabled()) - { - logger.debug("Bound txn synch: " + txnSynch); - } - return txnSynch; - } - - /** - * Cleans out transaction resources if present - */ - private static void clearSynchronization() - { - if (TransactionSynchronizationManager.hasResource(RESOURCE_KEY_TXN_SYNCH)) - { - Object txnSynch = TransactionSynchronizationManager.unbindResource(RESOURCE_KEY_TXN_SYNCH); - // done - if (logger.isDebugEnabled()) - { - logger.debug("Unbound txn synch:" + txnSynch); - } - } - } - - /** - * Helper method to rebind the synchronization to the transaction - * - * @param txnSynch - */ - private static void rebindSynchronization(TransactionSynchronizationImpl txnSynch) - { - TransactionSynchronizationManager.bindResource(RESOURCE_KEY_TXN_SYNCH, txnSynch); - if (logger.isDebugEnabled()) - { - logger.debug("Bound txn synch: " + txnSynch); - } - } - - /** - * Handler of txn synchronization callbacks specific to internal - * application requirements - */ - private static class TransactionSynchronizationImpl extends TransactionSynchronizationAdapter - { - private long txnStartTime; - private final String txnId; - private final Set daoServices; - private final Set integrityCheckers; - private final Set lucenes; - private final LinkedHashSet listeners; - private final Set> transactionalCaches; - private final Map resources; - - /** - * Sets up the resource map - * - * @param txnId - */ - public TransactionSynchronizationImpl(String txnId) - { - this.txnStartTime = System.currentTimeMillis(); - this.txnId = txnId; - daoServices = new HashSet(3); - integrityCheckers = new HashSet(3); - lucenes = new HashSet(3); - listeners = new LinkedHashSet(5); - transactionalCaches = new HashSet>(3); - resources = new HashMap(17); - } - - public long getTransactionStartTime() - { - return txnStartTime; - } - - public String getTransactionId() - { - return txnId; - } - - /** - * @return Returns a set of TransactionalDao instances that will be called - * during end-of-transaction processing - */ - public Set getDaoServices() - { - return daoServices; - } - - /** - * @return Returns a set of IntegrityChecker instances that will be called - * during end-of-transaction processing - */ - public Set getIntegrityCheckers() - { - return integrityCheckers; - } - - /** - * @return Returns a set of LuceneIndexerAndSearcherFactory that will be called - * during end-of-transaction processing - */ - public Set getLucenes() - { - return lucenes; - } - - /** - * @return Returns a set of TransactionListener instances that will be called - * during end-of-transaction processing - */ - @SuppressWarnings("unchecked") - public boolean addListener(TransactionListener listener) - { - ParameterCheck.mandatory("listener", listener); - - if (listener instanceof TransactionalCache) - { - return transactionalCaches.add((TransactionalCache)listener); - } - else - { - return listeners.add(listener); - } - } - - /** - * @return Returns the listeners in a list disconnected from the original set - */ - private List getListenersIterable() - { - return new ArrayList(listeners); - } - - public String toString() - { - StringBuilder sb = new StringBuilder(50); - sb.append("TransactionSychronizationImpl") - .append("[ txnId=").append(txnId) - .append(", daos=").append(daoServices.size()) - .append(", integrity=").append(integrityCheckers.size()) - .append(", indexers=").append(lucenes.size()) - .append(", resources=").append(resources) - .append("]"); - return sb.toString(); - } - - /** - * @see AlfrescoTransactionSupport#SESSION_SYNCHRONIZATION_ORDER - */ - @Override - public int getOrder() - { - return AlfrescoTransactionSupport.SESSION_SYNCHRONIZATION_ORDER; - } - - @Override - public void suspend() - { - if (logger.isDebugEnabled()) - { - logger.debug("Suspending transaction: " + this); - } - AlfrescoTransactionSupport.clearSynchronization(); - } - - @Override - public void resume() - { - if (logger.isDebugEnabled()) - { - logger.debug("Resuming transaction: " + this); - } - AlfrescoTransactionSupport.rebindSynchronization(this); - } - - /** - * Pre-commit cleanup. - *

- * Ensures that the session transaction listeners are property executed. - * The Lucene indexes are then prepared. - */ - @Override - public void beforeCommit(boolean readOnly) - { - if (logger.isDebugEnabled()) - { - logger.debug("Before commit " + (readOnly ? "read-only" : "" ) + ": " + this); - } - // get the txn ID - TransactionSynchronizationImpl synch = (TransactionSynchronizationImpl) - TransactionSynchronizationManager.getResource(RESOURCE_KEY_TXN_SYNCH); - if (synch == null) - { - throw new AlfrescoRuntimeException("No synchronization bound to thread"); - } - - // These are still considered part of the transaction so are executed here - doBeforeCommit(readOnly); - - // Check integrity - for (IntegrityChecker integrityChecker : integrityCheckers) - { - integrityChecker.checkIntegrity(); - } - - // prepare the indexes - for (LuceneIndexerAndSearcher lucene : lucenes) - { - lucene.prepare(); - } - - // Flush the DAOs - for (TransactionalDao dao : daoServices) - { - dao.beforeCommit(readOnly); - } - - // Flush the transactional caches - for (TransactionalCache cache : transactionalCaches) - { - cache.beforeCommit(readOnly); - } - } - - /** - * Execute the beforeCommit event handlers for the registered listeners - * - * @param readOnly is read only - */ - private void doBeforeCommit(boolean readOnly) - { - doBeforeCommit(new HashSet(listeners.size()), readOnly); - } - - /** - * Executes the beforeCommit event handlers for the outstanding listeners. - * This process is iterative as the process of calling listeners may lead to more listeners - * being added. The new listeners will be processed until there no listeners remaining. - * - * @param visitedListeners a set containing the already visited listeners - * @param readOnly is read only - */ - private void doBeforeCommit(Set visitedListeners, boolean readOnly) - { - Set pendingListeners = new HashSet(listeners); - pendingListeners.removeAll(visitedListeners); - - if (pendingListeners.size() != 0) - { - for (TransactionListener listener : pendingListeners) - { - listener.beforeCommit(readOnly); - visitedListeners.add(listener); - } - - doBeforeCommit(visitedListeners, readOnly); - } - } - - @Override - public void beforeCompletion() - { - if (logger.isDebugEnabled()) - { - logger.debug("Before completion: " + this); - } - // notify listeners - for (TransactionListener listener : getListenersIterable()) - { - listener.beforeCompletion(); - } - } - - - @Override - public void afterCompletion(int status) - { - String statusStr = "unknown"; - switch (status) - { - case TransactionSynchronization.STATUS_COMMITTED: - statusStr = "committed"; - break; - case TransactionSynchronization.STATUS_ROLLED_BACK: - statusStr = "rolled-back"; - break; - default: - } - if (logger.isDebugEnabled()) - { - logger.debug("After completion (" + statusStr + "): " + this); - } - - // Force any queries for read-write state to return TXN_READ_ONLY - // This will be cleared with the synchronization, so we don't need to clear it out - AlfrescoTransactionSupport.bindResource(RESOURCE_KEY_TXN_COMPLETING, Boolean.TRUE); - - // Clean up the transactional caches - for (TransactionalCache cache : transactionalCaches) - { - try - { - if (status == TransactionSynchronization.STATUS_COMMITTED) - { - cache.afterCommit(); - } - else - { - cache.afterRollback(); - } - } - catch (RuntimeException e) - { - logger.error("After completion (" + statusStr + ") TransactionalCache exception", e); - } - } - - // commit/rollback Lucene - for (LuceneIndexerAndSearcher lucene : lucenes) - { - try - { - if (status == TransactionSynchronization.STATUS_COMMITTED) - { - lucene.commit(); - } - else - { - lucene.rollback(); - } - } - catch (RuntimeException e) - { - logger.error("After completion (" + statusStr + ") Lucene exception", e); - } - } - - List iterableListeners = getListenersIterable(); - // notify listeners - if (status == TransactionSynchronization.STATUS_COMMITTED) - { - for (TransactionListener listener : iterableListeners) - { - try - { - listener.afterCommit(); - } - catch (RuntimeException e) - { - logger.error("After completion (" + statusStr + ") listener exception: \n" + - " listener: " + listener, - e); - } - } - } - else - { - for (TransactionListener listener : iterableListeners) - { - try - { - listener.afterRollback(); - } - catch (RuntimeException e) - { - logger.error("After completion (" + statusStr + ") listener exception: \n" + - " listener: " + listener, - e); - } - } - } - - // clear the thread's registrations and synchronizations - AlfrescoTransactionSupport.clearSynchronization(); - } - } -} + +} diff --git a/source/java/org/alfresco/repo/transaction/DAOAdapter.java b/source/java/org/alfresco/repo/transaction/DAOAdapter.java new file mode 100644 index 0000000000..4bfb2ddada --- /dev/null +++ b/source/java/org/alfresco/repo/transaction/DAOAdapter.java @@ -0,0 +1,67 @@ +package org.alfresco.repo.transaction; + +/* package scope */ class DAOAdapter implements TransactionListener +{ + protected TransactionalDao daoService; + + public DAOAdapter (TransactionalDao daoService) + { + this.daoService = daoService; + } + + @Override + public void flush() + { + // NO-OP + } + + @Override + public void beforeCommit(boolean readOnly) + { + daoService.beforeCommit(readOnly); + } + + @Override + public void beforeCompletion() + { + // NO-OP + } + + @Override + public void afterCommit() + { + // NO-OP + } + + @Override + public void afterRollback() + { + // NO-OP + } + + public TransactionalDao getService() + { + return daoService; + } + + public boolean equals(Object obj) + { + if(obj instanceof DAOAdapter) + { + DAOAdapter other = (DAOAdapter)obj; + return daoService.equals(other.daoService); + } + return daoService.equals(obj); + } + + /** + * Return a hashcode for the request + * + * @return int + */ + public int hashCode() + { + return daoService.hashCode(); + } +} + diff --git a/source/java/org/alfresco/repo/transaction/LuceneIndexerAndSearcherAdapter.java b/source/java/org/alfresco/repo/transaction/LuceneIndexerAndSearcherAdapter.java new file mode 100644 index 0000000000..95c1f93d36 --- /dev/null +++ b/source/java/org/alfresco/repo/transaction/LuceneIndexerAndSearcherAdapter.java @@ -0,0 +1,68 @@ +package org.alfresco.repo.transaction; + +import org.alfresco.repo.search.impl.lucene.LuceneIndexerAndSearcher; + +/* package */class LuceneIndexerAndSearcherAdapter implements TransactionListener +{ + + protected LuceneIndexerAndSearcher luceneIndexerAndSearcher; + + public LuceneIndexerAndSearcherAdapter (LuceneIndexerAndSearcher luceneIndexerAndSearcher) + { + this.luceneIndexerAndSearcher = luceneIndexerAndSearcher; + } + + @Override + public void flush() + { + // NO-OP + } + + @Override + public void beforeCommit(boolean readOnly) + { + luceneIndexerAndSearcher.prepare(); + } + + @Override + public void beforeCompletion() + { + // NO-OP + } + + @Override + public void afterCommit() + { + luceneIndexerAndSearcher.commit(); + } + + @Override + public void afterRollback() + { + luceneIndexerAndSearcher.rollback(); + } + + /** + * Return a hashcode for the request + * + * @return int + */ + public int hashCode() + { + return luceneIndexerAndSearcher.hashCode(); + } + + public boolean equals(Object obj) + { + if(obj instanceof LuceneIndexerAndSearcherAdapter) + { + LuceneIndexerAndSearcherAdapter other = (LuceneIndexerAndSearcherAdapter)obj; + return luceneIndexerAndSearcher.equals(other.luceneIndexerAndSearcher); + } + return luceneIndexerAndSearcher.equals(obj); + } + + + + +} diff --git a/source/java/org/alfresco/repo/transaction/TransactionListener.java b/source/java/org/alfresco/repo/transaction/TransactionListener.java index 04c93eed80..2ed6c86c95 100644 --- a/source/java/org/alfresco/repo/transaction/TransactionListener.java +++ b/source/java/org/alfresco/repo/transaction/TransactionListener.java @@ -25,7 +25,7 @@ package org.alfresco.repo.transaction; * * @author Derek Hulley */ -public interface TransactionListener +public interface TransactionListener extends org.alfresco.util.transaction.TransactionListener { /** * @deprecated No longer supported @@ -71,4 +71,5 @@ public interface TransactionListener * be used only for cleaning up resources after a rollback has occured. */ void afterRollback(); + }