Merged PATCHES/V4.1.3 to HEAD

45612: Fix for ALF-17456 	 BM-0013: SOAK01_04: 150K+ calls to AuthorityDaoImpl.isAuthorityContained in on login
    45648: Final part for     ALF-17456 BM-0013: SOAK01_04: 150K+ calls to AuthorityDaoImpl.isAuthorityContained in on login 
    46033: Build fixes


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@47861 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Andrew Hind 2013-03-11 12:36:32 +00:00
parent c1d11e0f3a
commit 01b6ae39b1
19 changed files with 1517 additions and 55 deletions

View File

@ -100,8 +100,8 @@
<property name="authorityBridgeDAO">
<ref bean="authorityBridgeDAO" />
</property>
<property name="authorityBridgeTableByTenantCache">
<ref bean="authorityBridgeTableByTenantCache" />
<property name="authorityBridgeTableCache">
<ref bean="authorityBridgeTableCache" />
</property>
<property name="useBridgeTable">
<value>${authority.useBridgeTable}</value>

View File

@ -146,11 +146,45 @@
<!-- Authority BridgeTable cache -->
<!-- =========================== -->
<!-- The cross-transaction shared cache for authority bridge -->
<bean name="authorityBridgeTableByTenantSharedCache" class="org.alfresco.repo.cache.DefaultSimpleCache">
<property name="maxItems" value="${cache.authorityBridgeTableByTenantSharedCache.maxItems}"/>
<bean id="asynchronouslyRefreshedCacheThreadPoolExecutor" class="org.alfresco.util.ThreadPoolExecutorFactoryBean">
<property name="poolName">
<value>asynchronouslyRefreshedCacheThreadPool</value>
</property>
<property name="corePoolSize">
<value>1</value>
</property>
<property name="maximumPoolSize">
<value>1</value>
</property>
<property name="threadPriority">
<value>5</value>
</property>
</bean>
<bean name="asynchronouslyRefreshedCacheRegistry" class="org.alfresco.repo.cache.DefaultAsynchronouslyRefreshedCacheRegistry" >
</bean>
<bean name="abstractAsynchronouslyRefreshedCache" class="org.alfresco.repo.cache.AbstractAsynchronouslyRefreshedCache" abstract="true">
<property name="threadPoolExecutor">
<ref bean="asynchronouslyRefreshedCacheThreadPoolExecutor" />
</property>
<property name="tenantService">
<ref bean="tenantService" />
</property>
<property name="registry">
<ref bean="asynchronouslyRefreshedCacheRegistry" />
</property>
</bean>
<bean name="authorityBridgeTableCache" class="org.alfresco.repo.security.authority.AuthorityBridgeTableAsynchronouslyRefreshedCache" parent="abstractAsynchronouslyRefreshedCache">
<property name="authorityBridgeDAO">
<ref bean="authorityBridgeDAO" />
</property>
<property name="retryingTransactionHelper">
<ref bean="retryingTransactionHelper" />
</property>
</bean>
<!-- ================================================ -->
<!-- Zone lookup to ChildAssociationRefs -->

View File

@ -979,7 +979,6 @@ cache.immutableSingletonSharedCache.maxItems=12000
cache.remoteAlfrescoTicketService.ticketsCache.maxItems=1000
cache.contentDiskDriver.fileInfoCache.maxItems=1000
cache.globalConfigSharedCache.maxItems=1000
cache.authorityBridgeTableByTenantSharedCache.maxItems=10
#
# Download Service Limits, in bytes

View File

@ -220,21 +220,8 @@
<property name="disableSharedCache" value="${system.cache.disableMutableSharedCaches}" />
</bean>
<!-- The transactional cache for authority bridge -->
<bean name="authorityBridgeTableByTenantCache" class="org.alfresco.repo.cache.TransactionalCache">
<property name="sharedCache">
<ref bean="authorityBridgeTableByTenantSharedCache" />
</property>
<property name="name">
<value>org.alfresco.authorityBridgeTableByTenantTransactionalCache</value>
</property>
<property name="maxCacheSize" value="10" />
<property name="mutable" value="true" />
<property name="allowEqualsChecks" value="true" />
<property name="disableSharedCache" value="${system.cache.disableMutableSharedCaches}" />
</bean>
<!-- The transactional cache for authority containers -->
<bean name="zoneToAuthorityCache" class="org.alfresco.repo.cache.TransactionalCache">

View File

@ -0,0 +1,802 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.cache;
import java.util.Collections;
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
*/
public abstract class AbstractAsynchronouslyRefreshedCache<T> implements AsynchronouslyRefreshedCache<T>, RefreshableCacheListener, Callable<Void>, BeanNameAware,
InitializingBean, TransactionListener
{
private static Log logger = LogFactory.getLog(AbstractAsynchronouslyRefreshedCache.class);
private static final String RESOURCE_KEY_TXN_DATA = "AbstractAsynchronouslyRefreshedCache.TxnData";
private List<RefreshableCacheListener> listeners = new LinkedList<RefreshableCacheListener>();
/*
* (non-Javadoc)
* @see org.alfresco.repo.cache.AsynchronouslyRefreshedCacheRegistry#register(org.alfresco.repo.cache.
* RefreshableCacheListener)
*/
@Override
public void register(RefreshableCacheListener listener)
{
listeners.add(listener);
}
private enum RefreshState
{
IDLE, WAITING, RUNNING, DONE
};
private ThreadPoolExecutor threadPoolExecutor;
private AsynchronouslyRefreshedCacheRegistry registry;
private TenantService tenantService;
// State
private final ReentrantReadWriteLock liveLock = new ReentrantReadWriteLock();
private final ReentrantReadWriteLock refreshLock = new ReentrantReadWriteLock();
private final ReentrantReadWriteLock runLock = new ReentrantReadWriteLock();
private HashMap<String, T> live = new HashMap<String, T>();
private LinkedHashSet<Refresh> refreshQueue = new LinkedHashSet<Refresh>();
private String cacheId;
private RefreshState refreshState = RefreshState.IDLE;
private String resourceKeyTxnData;
/**
* @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);
}
/*
* (non-Javadoc)
* @see org.alfresco.repo.cache.RefreshableCache#get()
*/
@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)
{
}
}
}
}
/*
* (non-Javadoc)
* @see org.alfresco.repo.cache.RefreshableCache#refresh()
*/
@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);
}
/*
* (non-Javadoc)
* @see org.alfresco.repo.cache.RefreshableCacheListener#onRefreshableCacheEvent()
*/
@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<String> tenantIds = new LinkedHashSet<String>();
tenantIds.add(refreshableCacheEvent.getTenantId());
queueRefreshAndSubmit(tenantIds);
}
}
/**
* To be used in a transaction only.
*/
private TransactionData getTransactionData()
{
@SuppressWarnings("unchecked")
TransactionData data = (TransactionData) AlfrescoTransactionSupport.getResource(resourceKeyTxnData);
if (data == null)
{
data = new TransactionData();
// create and initialize caches
data.tenantIds = new LinkedHashSet<String>();
// 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<String> tenantIds)
{
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();
}
/**
* @return
*/
public boolean isUpToDate()
{
String tenantId = tenantService.getCurrentUserDomain();
refreshLock.readLock().lock();
try
{
for(Refresh refresh : refreshQueue)
{
if(refresh.getTenantId().equals(tenantId))
{
return false;
}
}
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();
}
}
/*
* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public Void call()
{
try
{
doCall();
return null;
}
catch (Exception e)
{
logger.warn("Cache update faiiled", e);
runLock.writeLock().lock();
try
{
threadPoolExecutor.submit(this);
refreshState = RefreshState.WAITING;
}
finally
{
runLock.writeLock().unlock();
}
return null;
}
}
/**
* @return
* @throws Exception
*/
private void doCall() throws Exception
{
Refresh refresh = setUpRefresh();
if (logger.isDebugEnabled())
{
logger.debug("Building cache for tenant" + refresh.getTenantId());
}
if (refresh == null)
{
return;
}
try
{
doRefresh(refresh);
}
catch (Exception e)
{
refresh.setState(RefreshState.WAITING);
throw e;
}
}
/**
* @param refresh
* @return
*/
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());
}
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();
}
broadcastEvent(new RefreshableCacheRefreshedEvent(cacheId, refresh.tenantId));
}
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();
}
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.BeanNameAware#setBeanName(java.lang.String)
*/
@Override
public void setBeanName(String name)
{
cacheId = name;
}
/*
* (non-Javadoc)
* @see org.alfresco.repo.cache.AsynchronouslyRefreshedCache#getCacheId()
*/
@Override
public String getCacheId()
{
return cacheId;
}
/**
* Build the cache entry for the specific tenant.
*
* @param tenantId
* @return
*/
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;
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@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;
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@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;
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
return "Refresh [tenantId=" + tenantId + ", state=" + state + ", hashCode()=" + hashCode() + "]";
}
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
@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);
}
}
/*
* (non-Javadoc)
* @see org.alfresco.repo.transaction.TransactionListener#flush()
*/
@Override
public void flush()
{
// Nothing
}
/*
* (non-Javadoc)
* @see org.alfresco.repo.transaction.TransactionListener#beforeCommit(boolean)
*/
@Override
public void beforeCommit(boolean readOnly)
{
// Nothing
}
/*
* (non-Javadoc)
* @see org.alfresco.repo.transaction.TransactionListener#beforeCompletion()
*/
@Override
public void beforeCompletion()
{
// Nothing
}
/*
* (non-Javadoc)
* @see org.alfresco.repo.transaction.TransactionListener#afterCommit()
*/
@Override
public void afterCommit()
{
TransactionData txnData = getTransactionData();
queueRefreshAndSubmit(txnData.tenantIds);
}
/*
* (non-Javadoc)
* @see org.alfresco.repo.transaction.TransactionListener#afterRollback()
*/
@Override
public void afterRollback()
{
// Nothing
}
private static class TransactionData
{
LinkedHashSet<String> tenantIds;
}
}

View File

@ -0,0 +1,73 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.cache;
/**
* A generic event with the cache id and affected tenant
*
* @author Andy
*/
public abstract class AbstractRefreshableCacheEvent implements RefreshableCacheEvent
{
/**
*
*/
private static final long serialVersionUID = 1324638640132648062L;
private String cacheId;
private String tenantId;
AbstractRefreshableCacheEvent(String cacheId, String tenantId)
{
this.cacheId = cacheId;
this.tenantId = tenantId;
}
/*
* (non-Javadoc)
* @see org.alfresco.repo.cache.RefreshableCacheEvent#getCacheId()
*/
@Override
public String getCacheId()
{
return cacheId;
}
/*
* (non-Javadoc)
* @see org.alfresco.repo.cache.RefreshableCacheEvent#getTenantId()
*/
@Override
public String getTenantId()
{
return tenantId;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
return "AbstractRefreshableCacheEvent [cacheId=" + cacheId + ", tenantId=" + tenantId + "]";
}
}

View File

@ -0,0 +1,37 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.cache;
/**
* Implementation details in addition to the exposed interface.
*
* @author Andy
*
*/
public interface AsynchronouslyRefreshedCache<T> extends RefreshableCache<T>
{
/**
* Get the cache id
*
* @return
*/
String getCacheId();
}

View File

@ -0,0 +1,41 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.cache;
/**
* A registry of all AsynchronouslyRefreshedCaches to be used for notification.
*
* @author Andy
*
*/
public interface AsynchronouslyRefreshedCacheRegistry
{
/**
* Register a listener
* @param listener
*/
public void register(RefreshableCacheListener listener);
/**
* Fire an even
* @param event
* @param toAll - true goes to all listeners, false only to listeners that have a matching cacheId
*/
public void broadcastEvent(RefreshableCacheEvent event, boolean toAll);
}

View File

@ -0,0 +1,83 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
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
* (Should be genericised somewhere..)
*
* @author Andy
*/
public class DefaultAsynchronouslyRefreshedCacheRegistry implements AsynchronouslyRefreshedCacheRegistry
{
private static Log logger = LogFactory.getLog(DefaultAsynchronouslyRefreshedCacheRegistry.class);
private List<RefreshableCacheListener> listeners = new LinkedList<RefreshableCacheListener>();
/*
* (non-Javadoc)
* @see org.alfresco.repo.cache.AsynchronouslyRefreshedCacheRegistry#register(org.alfresco.repo.cache.
* RefreshableCacheListener)
*/
@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 to "+listener);
}
listener.onRefreshableCacheEvent(event);
}
else
{
if (listener.getCacheId().equals(event.getCacheId()))
{
if(logger.isDebugEnabled())
{
logger.debug("Specific Delivery to "+listener);
}
listener.onRefreshableCacheEvent(event);
}
}
}
}
}

View File

@ -0,0 +1,51 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.cache;
/**
* Basic cache API
*
* @author Andy
*
*/
public interface RefreshableCache <T>
{
/**
* Get the cache.
* If there is no cache value this call will block.
* If the underlying cache is being refreshed, the old cache value will be returned until the refresh is complete.
*
* @return
*/
public T get();
/**
* Refresh the cache asynchronously.
*/
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);
}

View File

@ -0,0 +1,42 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.cache;
import java.io.Serializable;
/**
* A cache event
*
* @author Andy
*
*/
public interface RefreshableCacheEvent extends Serializable
{
/**
* Get the cache id
* @return
*/
public String getCacheId();
/**
* Get the affected tenant id
* @return
*/
public String getTenantId();
}

View File

@ -0,0 +1,40 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.cache;
/**
* API to listen to cache events
*
* @author Andy
*
*/
public interface RefreshableCacheListener
{
/**
* Receive an event
* @param refreshableCacheEvent
*/
public void onRefreshableCacheEvent(RefreshableCacheEvent refreshableCacheEvent);
/**
* Cache id so broadcast can be constrained to matching caches
* @return
*/
public String getCacheId();
}

View File

@ -0,0 +1,42 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.cache;
/**
* Describes an entry that is stale in the cache
*
* @author Andy
*
*/
public class RefreshableCacheRefreshEvent extends AbstractRefreshableCacheEvent
{
/**
* @param cacheId
*/
RefreshableCacheRefreshEvent(String cacheId, String tenantId)
{
super(cacheId, tenantId);
}
/**
*
*/
private static final long serialVersionUID = -8011932788039835334L;
}

View File

@ -0,0 +1,44 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.cache;
/**
* Describes a new entry has been inserted in the cache.
*
* @author Andy
*
*/
public class RefreshableCacheRefreshedEvent extends AbstractRefreshableCacheEvent
{
/**
*
*/
private static final long serialVersionUID = 2352511592269578075L;
/**
* @param cacheId
* @param tenantId
*/
RefreshableCacheRefreshedEvent(String cacheId, String tenantId)
{
super(cacheId, tenantId);
}
}

View File

@ -258,8 +258,15 @@ public class TransactionalCache<K extends Serializable, V extends Object>
public boolean getDisableSharedCacheReadForTransaction()
{
TransactionData txnData = getTransactionData();
return txnData.noSharedCacheRead;
if (AlfrescoTransactionSupport.getTransactionId() != null)
{
TransactionData txnData = getTransactionData();
return txnData.noSharedCacheRead;
}
else
{
return false;
}
}
/**

View File

@ -144,7 +144,7 @@ public abstract class AbstractAuthorityBridgeDAO implements AuthorityBridgeDAO
storeId = nodeDAO.getStore(tenantSpecificStoreRef).getFirst();
}
Pair<Long, NodeRef> pair = (authRef == null) ? null : nodeDAO.getNodePair(authRef);
Pair<Long, NodeRef> pair = (authRef == null) ? null : nodeDAO.getNodePair(tenantService.getName(authRef));
return selectDirectAuthoritiesForUser(authorityContainerTypeQNameId, memberAssocQNameId, authorityNameQNameId, storeId, (pair == null) ? -1L : pair.getFirst());
}

View File

@ -0,0 +1,101 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.security.authority;
import java.util.List;
import org.alfresco.repo.cache.AbstractAsynchronouslyRefreshedCache;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.util.BridgeTable;
import org.alfresco.util.PropertyCheck;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
/**
* @author Andy
*/
public class AuthorityBridgeTableAsynchronouslyRefreshedCache extends AbstractAsynchronouslyRefreshedCache<BridgeTable<String>> implements InitializingBean
{
private static Log logger = LogFactory.getLog(AuthorityBridgeTableAsynchronouslyRefreshedCache.class);
private AuthorityBridgeDAO authorityBridgeDAO;
private RetryingTransactionHelper retryingTransactionHelper;
/**
* @param authorityBridgeDAO
* the authorityBridgeDAO to set
*/
public void setAuthorityBridgeDAO(AuthorityBridgeDAO authorityBridgeDAO)
{
this.authorityBridgeDAO = authorityBridgeDAO;
}
/**
* @param retryingTransactionHelper
* the retryingTransactionHelper to set
*/
public void setRetryingTransactionHelper(RetryingTransactionHelper retryingTransactionHelper)
{
this.retryingTransactionHelper = retryingTransactionHelper;
}
/*
* (non-Javadoc)
* @see org.alfresco.repo.cache.AbstractAsynchronouslyRefreshedCache#buildCache()
*/
@Override
protected BridgeTable<String> buildCache(final String tenantId)
{
return retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback<BridgeTable<String>>()
{
@Override
public BridgeTable<String> execute() throws Throwable
{
return doBuildCache(tenantId);
}
}, true, false);
}
private BridgeTable<String> doBuildCache(String tenantId)
{
List<AuthorityBridgeLink> links = authorityBridgeDAO.getAuthorityBridgeLinks();
BridgeTable<String> bridgeTable = new BridgeTable<String>();
for (AuthorityBridgeLink link : links)
{
bridgeTable.addLink(link.getParentName(), link.getChildName());
}
return bridgeTable;
}
/*
* (non-Javadoc)
* @see org.alfresco.repo.cache.AbstractAsynchronouslyRefreshedCache#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() throws Exception
{
PropertyCheck.mandatory(this, "authorityBridgeDAO", authorityBridgeDAO);
PropertyCheck.mandatory(this, "retryingTransactionHelper", retryingTransactionHelper);
super.afterPropertiesSet();
}
}

View File

@ -40,6 +40,8 @@ import org.alfresco.query.CannedQueryFactory;
import org.alfresco.query.CannedQueryResults;
import org.alfresco.query.PagingRequest;
import org.alfresco.query.PagingResults;
import org.alfresco.repo.cache.RefreshableCacheEvent;
import org.alfresco.repo.cache.RefreshableCacheListener;
import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.repo.domain.permissions.AclDAO;
import org.alfresco.repo.node.NodeServicePolicies;
@ -76,12 +78,14 @@ import org.alfresco.util.EqualsHelper;
import org.alfresco.util.ISO9075;
import org.alfresco.util.Pair;
import org.alfresco.util.ParameterCheck;
import org.alfresco.util.PropertyCheck;
import org.alfresco.util.SearchLanguageConversion;
import org.alfresco.util.registry.NamedObjectRegistry;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
public class AuthorityDAOImpl implements AuthorityDAO, NodeServicePolicies.BeforeDeleteNodePolicy, NodeServicePolicies.OnUpdatePropertiesPolicy
public class AuthorityDAOImpl implements AuthorityDAO, NodeServicePolicies.BeforeDeleteNodePolicy, NodeServicePolicies.OnUpdatePropertiesPolicy, RefreshableCacheListener, InitializingBean
{
private static Log logger = LogFactory.getLog(AuthorityDAOImpl.class);
@ -113,7 +117,7 @@ public class AuthorityDAOImpl implements AuthorityDAO, NodeServicePolicies.Befor
private SimpleCache<String, Set<String>> userAuthorityCache;
private SimpleCache<Pair<String, String>, List<ChildAssociationRef>> zoneAuthorityCache;
private SimpleCache<NodeRef, Pair<Map<NodeRef,String>, List<NodeRef>>> childAuthorityCache;
private SimpleCache<String, BridgeTable<String>> authorityBridgeTableByTenantCache;
private AuthorityBridgeTableAsynchronouslyRefreshedCache authorityBridgeTableCache;
/** System Container ref cache (Tennant aware) */
private Map<String, NodeRef> systemContainerRefs = new ConcurrentHashMap<String, NodeRef>(4);
@ -126,11 +130,14 @@ public class AuthorityDAOImpl implements AuthorityDAO, NodeServicePolicies.Befor
private boolean useBridgeTable = true;
private boolean useGetContainingAuthoritiesForIsAuthorityContained = true;
private AclDAO aclDao;
private PolicyComponent policyComponent;
private NamedObjectRegistry<CannedQueryFactory> cannedQueryRegistry;
private NamedObjectRegistry<CannedQueryFactory<?>> cannedQueryRegistry;
private AuthorityBridgeDAO authorityBridgeDAO;
public AuthorityDAOImpl()
{
super();
@ -197,9 +204,9 @@ public class AuthorityDAOImpl implements AuthorityDAO, NodeServicePolicies.Befor
this.childAuthorityCache = childAuthorityCache;
}
public void setAuthorityBridgeTableByTenantCache(SimpleCache<String, BridgeTable<String>> authorityBridgeTableByTenantCache)
public void setAuthorityBridgeTableCache(AuthorityBridgeTableAsynchronouslyRefreshedCache authorityBridgeTableCache)
{
this.authorityBridgeTableByTenantCache = authorityBridgeTableByTenantCache;
this.authorityBridgeTableCache = authorityBridgeTableCache;
}
/**
@ -231,11 +238,19 @@ public class AuthorityDAOImpl implements AuthorityDAO, NodeServicePolicies.Befor
this.policyComponent = policyComponent;
}
public void setCannedQueryRegistry(NamedObjectRegistry<CannedQueryFactory> cannedQueryRegistry)
public void setCannedQueryRegistry(NamedObjectRegistry<CannedQueryFactory<?>> cannedQueryRegistry)
{
this.cannedQueryRegistry = cannedQueryRegistry;
}
/**
* @param useGetContainingAuthoritiesForHasAuthority the useGetContainingAuthoritiesForHasAuthority to set
*/
public void setUseGetContainingAuthoritiesForIsAuthorityContained(boolean useGetContainingAuthoritiesForIsAuthorityContained)
{
this.useGetContainingAuthoritiesForIsAuthorityContained = useGetContainingAuthoritiesForIsAuthorityContained;
}
/**
* @param authorityBridgeDAO the authorityBridgeDAO to set
*/
@ -296,7 +311,7 @@ public class AuthorityDAOImpl implements AuthorityDAO, NodeServicePolicies.Befor
else
{
userAuthorityCache.clear();
authorityBridgeTableByTenantCache.clear();
authorityBridgeTableCache.refresh();
}
}
@ -352,7 +367,7 @@ public class AuthorityDAOImpl implements AuthorityDAO, NodeServicePolicies.Befor
removeParentsFromChildAuthorityCache(nodeRef);
authorityLookupCache.remove(cacheKey(name));
userAuthorityCache.clear();
authorityBridgeTableByTenantCache.clear();
authorityBridgeTableCache.refresh();
nodeService.deleteNode(nodeRef);
}
@ -724,30 +739,14 @@ public class AuthorityDAOImpl implements AuthorityDAO, NodeServicePolicies.Befor
else
{
userAuthorityCache.clear();
authorityBridgeTableByTenantCache.clear();
authorityBridgeTableCache.refresh();
}
}
private BridgeTable<String> getBridgeTable()
{
String tenant = tenantService.getCurrentUserDomain();
BridgeTable<String> bridgeTable = authorityBridgeTableByTenantCache.get(tenant);
if(bridgeTable == null)
{
List<AuthorityBridgeLink> links = authorityBridgeDAO.getAuthorityBridgeLinks();
bridgeTable = new BridgeTable<String>();
for(AuthorityBridgeLink link : links)
{
bridgeTable.addLink(link.getParentName(), link.getChildName());
}
authorityBridgeTableByTenantCache.put(tenant, bridgeTable);
}
return bridgeTable;
}
private void listAuthoritiesByBridgeTable(Set<String> authorities, String name)
{
BridgeTable<String> bridgeTable = getBridgeTable();
BridgeTable<String> bridgeTable = authorityBridgeTableCache.get();
AuthorityType type = AuthorityType.getAuthorityType(name);
switch(type)
@ -1095,8 +1094,21 @@ public class AuthorityDAOImpl implements AuthorityDAO, NodeServicePolicies.Befor
negativeHits.add(getPooledName(authority));
return false;
}
return isAuthorityContained(authorityNodeRef, getPooledName(authority), authorityToFind, positiveHits, negativeHits);
if(useGetContainingAuthoritiesForIsAuthorityContained)
{
if(authorityBridgeTableCache.isUpToDate())
{
return getContainingAuthorities(null, authorityToFind, false).contains(authority);
}
else
{
return isAuthorityContained(authorityNodeRef, getPooledName(authority), authorityToFind, positiveHits, negativeHits);
}
}
else
{
return isAuthorityContained(authorityNodeRef, getPooledName(authority), authorityToFind, positiveHits, negativeHits);
}
}
private boolean isAuthorityContained(NodeRef authorityNodeRef, String authority, String authorityToFind, Set<String> positiveHits, Set<String> negativeHits)
@ -1492,7 +1504,7 @@ public class AuthorityDAOImpl implements AuthorityDAO, NodeServicePolicies.Befor
}
}
authorityLookupCache.clear();
authorityBridgeTableByTenantCache.clear();
authorityBridgeTableCache.refresh();
// Cache is out of date
userAuthorityCache.clear();
@ -1621,5 +1633,57 @@ public class AuthorityDAOImpl implements AuthorityDAO, NodeServicePolicies.Befor
}
return auths;
}
}
/* (non-Javadoc)
* @see org.alfresco.repo.cache.RefreshableCacheListener#onRefreshableCacheEvent(org.alfresco.repo.cache.RefreshableCacheEvent)
*/
@Override
public void onRefreshableCacheEvent(RefreshableCacheEvent refreshableCacheEvent)
{
if(logger.isDebugEnabled())
{
logger.debug("Bridge Table cache triggering userAuthorityCache.clear()");
}
userAuthorityCache.clear();
}
/* (non-Javadoc)
* @see org.alfresco.repo.cache.RefreshableCacheListener#getCacheId()
*/
@Override
public String getCacheId()
{
return AuthorityDAOImpl.class.getName();
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() throws Exception
{
PropertyCheck.mandatory(this, "aclDao", aclDao);
PropertyCheck.mandatory(this, "authorityBridgeDAO", authorityBridgeDAO);
PropertyCheck.mandatory(this, "authorityBridgeTableCache", authorityBridgeTableCache);
PropertyCheck.mandatory(this, "authorityLookupCache", authorityLookupCache);
PropertyCheck.mandatory(this, "cannedQueryRegistry", cannedQueryRegistry);
PropertyCheck.mandatory(this, "childAuthorityCache", childAuthorityCache);
PropertyCheck.mandatory(this, "dictionaryService", dictionaryService);
PropertyCheck.mandatory(this, "namespacePrefixResolver", namespacePrefixResolver);
PropertyCheck.mandatory(this, "nodeService", nodeService);
PropertyCheck.mandatory(this, "personService", personService);
PropertyCheck.mandatory(this, "policyComponent", policyComponent);
PropertyCheck.mandatory(this, "searchService", searchService);
PropertyCheck.mandatory(this, "storeRef", storeRef);
PropertyCheck.mandatory(this, "tenantService", tenantService);
PropertyCheck.mandatory(this, "userAuthorityCache", userAuthorityCache);
PropertyCheck.mandatory(this, "zoneAuthorityCache", zoneAuthorityCache);
PropertyCheck.mandatory(this, "storeRef", storeRef);
PropertyCheck.mandatory(this, "storeRef", storeRef);
authorityBridgeTableCache.register(this);
};
}

View File

@ -74,6 +74,7 @@ public class AuthorityServiceTest extends TestCase
private UserTransaction tx;
private AclDAO aclDaoComponent;
private NodeService nodeService;
private AuthorityBridgeTableAsynchronouslyRefreshedCache authorityBridgeTableCache;
public AuthorityServiceTest()
{
@ -105,6 +106,7 @@ public class AuthorityServiceTest extends TestCase
authenticationDAO = (MutableAuthenticationDao) ctx.getBean("authenticationDao");
aclDaoComponent = (AclDAO) ctx.getBean("aclDAO");
nodeService = (NodeService) ctx.getBean("nodeService");
authorityBridgeTableCache = (AuthorityBridgeTableAsynchronouslyRefreshedCache) ctx.getBean("authorityBridgeTableCache");
String defaultAdminUser = AuthenticationUtil.getAdminUserName();
AuthenticationUtil.setFullyAuthenticatedUser(defaultAdminUser);
@ -623,16 +625,19 @@ public class AuthorityServiceTest extends TestCase
assertEquals(ROOT_GRP_CNT+2, pubAuthorityService.getAllRootAuthorities(AuthorityType.GROUP).size());
auth3 = pubAuthorityService.createAuthority(AuthorityType.GROUP, "three");
pubAuthorityService.addAuthority(auth1, auth3);
authorityBridgeTableCache.forceInChangesForThisUncommittedTransaction();
assertEquals("GROUP_three", auth3);
assertEquals(GRP_CNT+3, getAllAuthorities(AuthorityType.GROUP).size());
assertEquals(ROOT_GRP_CNT+2, pubAuthorityService.getAllRootAuthorities(AuthorityType.GROUP).size());
auth4 = pubAuthorityService.createAuthority(AuthorityType.GROUP, "four");
pubAuthorityService.addAuthority(auth1, auth4);
authorityBridgeTableCache.forceInChangesForThisUncommittedTransaction();
assertEquals("GROUP_four", auth4);
assertEquals(GRP_CNT+4, getAllAuthorities(AuthorityType.GROUP).size());
assertEquals(ROOT_GRP_CNT+2, pubAuthorityService.getAllRootAuthorities(AuthorityType.GROUP).size());
auth5 = pubAuthorityService.createAuthority(AuthorityType.GROUP, "five");
pubAuthorityService.addAuthority(auth2, auth5);
authorityBridgeTableCache.forceInChangesForThisUncommittedTransaction();
assertEquals("GROUP_five", auth5);
assertEquals(GRP_CNT+5, getAllAuthorities(AuthorityType.GROUP).size());
assertEquals(ROOT_GRP_CNT+2, pubAuthorityService.getAllRootAuthorities(AuthorityType.GROUP).size());
@ -640,6 +645,7 @@ public class AuthorityServiceTest extends TestCase
//System.out.println("Users: "+ getAllAuthorities(AuthorityType.USER));
checkAuthorityCollectionSize(3, getAllAuthorities(AuthorityType.USER), AuthorityType.USER);
pubAuthorityService.addAuthority(auth5, "andy");
authorityBridgeTableCache.forceInChangesForThisUncommittedTransaction();
assertEquals(GRP_CNT+5, getAllAuthorities(AuthorityType.GROUP).size());
assertEquals(ROOT_GRP_CNT+2, pubAuthorityService.getAllRootAuthorities(AuthorityType.GROUP).size());
// The next call looks for people not users :-)
@ -658,6 +664,7 @@ public class AuthorityServiceTest extends TestCase
assertTrue(pubAuthorityService.getContainedAuthorities(null, auth5, false).contains("andy"));
pubAuthorityService.removeAuthority(auth5, "andy");
authorityBridgeTableCache.forceInChangesForThisUncommittedTransaction();
assertEquals(GRP_CNT+5, getAllAuthorities(AuthorityType.GROUP).size());
assertEquals(ROOT_GRP_CNT+2, pubAuthorityService.getAllRootAuthorities(AuthorityType.GROUP).size());
// The next call looks for people not users :-)
@ -700,12 +707,14 @@ public class AuthorityServiceTest extends TestCase
assertEquals(ROOT_GRP_CNT+2, pubAuthorityService.getAllRootAuthorities(AuthorityType.GROUP).size());
auth5 = pubAuthorityService.createAuthority(AuthorityType.GROUP, "five");
pubAuthorityService.addAuthority(auth2, auth5);
authorityBridgeTableCache.forceInChangesForThisUncommittedTransaction();
assertEquals(GRP_CNT+5, getAllAuthorities(AuthorityType.GROUP).size());
assertEquals(ROOT_GRP_CNT+2, pubAuthorityService.getAllRootAuthorities(AuthorityType.GROUP).size());
assertEquals(3, getAllAuthorities(AuthorityType.USER).size());
pubAuthorityService.addAuthority(auth5, "andy");
pubAuthorityService.addAuthority(auth1, "andy");
authorityBridgeTableCache.forceInChangesForThisUncommittedTransaction();
assertEquals(GRP_CNT+5, getAllAuthorities(AuthorityType.GROUP).size());
assertEquals(ROOT_GRP_CNT+2, pubAuthorityService.getAllRootAuthorities(AuthorityType.GROUP).size());
@ -725,6 +734,7 @@ public class AuthorityServiceTest extends TestCase
assertTrue(pubAuthorityService.getContainedAuthorities(null, auth1, false).contains("andy"));
pubAuthorityService.removeAuthority(auth1, "andy");
authorityBridgeTableCache.forceInChangesForThisUncommittedTransaction();
assertEquals(GRP_CNT+5, getAllAuthorities(AuthorityType.GROUP).size());
assertEquals(ROOT_GRP_CNT+2, pubAuthorityService.getAllRootAuthorities(AuthorityType.GROUP).size());
@ -776,6 +786,7 @@ public class AuthorityServiceTest extends TestCase
checkAuthorityCollectionSize(3, getAllAuthorities(AuthorityType.USER), AuthorityType.USER);
pubAuthorityService.addAuthority(auth5, "andy");
pubAuthorityService.addAuthority(auth1, "andy");
authorityBridgeTableCache.forceInChangesForThisUncommittedTransaction();
assertEquals(GRP_CNT+5, getAllAuthorities(AuthorityType.GROUP).size());
assertEquals(ROOT_GRP_CNT+2, pubAuthorityService.getAllRootAuthorities(AuthorityType.GROUP).size());
@ -795,6 +806,7 @@ public class AuthorityServiceTest extends TestCase
assertTrue(pubAuthorityService.getContainedAuthorities(null, auth1, false).contains("andy"));
pubAuthorityService.addAuthority(auth3, auth2);
authorityBridgeTableCache.forceInChangesForThisUncommittedTransaction();
assertEquals(GRP_CNT+5, getAllAuthorities(AuthorityType.GROUP).size());
@ -910,6 +922,7 @@ public class AuthorityServiceTest extends TestCase
pubAuthorityService.addAuthority(auth6, "andy1");
pubAuthorityService.addAuthority(auth6, "andy5");
pubAuthorityService.addAuthority(auth6, "andy6");
authorityBridgeTableCache.forceInChangesForThisUncommittedTransaction();
assertEquals(2, pubAuthorityService.getContainedAuthorities(null, auth1, true).size());
assertEquals(11, pubAuthorityService.getContainedAuthorities(null, auth1, false).size());
@ -1079,6 +1092,8 @@ public class AuthorityServiceTest extends TestCase
String auth6 = pubAuthorityService.createAuthority(AuthorityType.GROUP, "six");
pubAuthorityService.addAuthority(auth6, "an3dy");
authorityBridgeTableCache.forceInChangesForThisUncommittedTransaction();
assertEquals(1, pubAuthorityService.getContainedAuthorities(null, auth1, true).size());
assertTrue(pubAuthorityService.getContainedAuthorities(null, auth1, true).contains("1234"));
assertEquals(1, pubAuthorityService.getContainedAuthorities(null, auth2, true).size());