/* * Copyright (C) 2005 Alfresco, Inc. * * Licensed under the Mozilla Public License version 1.1 * with a permitted attribution clause. You may obtain a * copy of the License at * * http://www.alfresco.org/legal/license.txt * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied. See the License for the specific * language governing permissions and limitations under the * License. */ package org.alfresco.repo.policy; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Policy Factory with caching support. * * @author David Caruana * * @param the type of Binding * @param

the type of Policy */ /*package*/ class CachedPolicyFactory extends PolicyFactory { // Logger private static final Log logger = LogFactory.getLog(PolicyComponentImpl.class); // Behaviour Filter private BehaviourFilter behaviourFilter = null; // Cache Lock private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); /** * Cache for a single Policy interface (keyed by Binding) */ private Map singleCache = new HashMap(); /** * Cache for a collection of Policy interfaces (keyed by Binding) */ private Map> listCache = new HashMap>(); /** * Construct cached policy factory * * @param policyClass the policy interface class * @param index the behaviour index to search on */ /*package*/ CachedPolicyFactory(Class

policyClass, BehaviourIndex index) { super(policyClass, index); behaviourFilter = index.getFilter(); // Register this cached policy factory as a change observer of the behaviour index // to allow for cache to be cleared appropriately. index.addChangeObserver(new BehaviourChangeObserver() { public void addition(B binding, Behaviour behaviour) { clearCache("aggregate delegate", singleCache, binding); clearCache("delegate collection", listCache, binding); } }); } @Override public P create(B binding) { // When behaviour filters are activated bypass the cache if (behaviourFilter != null && behaviourFilter.isActivated()) { return super.create(binding); } lock.readLock().lock(); try { P policyInterface = singleCache.get(binding); if (policyInterface == null) { // Upgrade read lock to write lock lock.readLock().unlock(); lock.writeLock().lock(); try { // Check again policyInterface = singleCache.get(binding); if (policyInterface == null) { policyInterface = super.create(binding); singleCache.put(binding, policyInterface); if (logger.isDebugEnabled()) logger.debug("Cached delegate interface " + policyInterface + " for " + binding + " and policy " + getPolicyClass()); } } finally { // Downgrade lock to read lock.readLock().lock(); lock.writeLock().unlock(); } } return policyInterface; } finally { lock.readLock().unlock(); } } @Override public Collection

createList(B binding) { // When behaviour filters are activated bypass the cache if (behaviourFilter != null && behaviourFilter.isActivated()) { return super.createList(binding); } lock.readLock().lock(); try { Collection

policyInterfaces = listCache.get(binding); if (policyInterfaces == null) { // Upgrade read lock to write lock lock.readLock().unlock(); lock.writeLock().lock(); try { // Check again policyInterfaces = listCache.get(binding); if (policyInterfaces == null) { policyInterfaces = super.createList(binding); listCache.put(binding, policyInterfaces); if (logger.isDebugEnabled()) logger.debug("Cached delegate interface collection " + policyInterfaces + " for " + binding + " and policy " + getPolicyClass()); } } finally { // Downgrade lock to read lock.readLock().lock(); lock.writeLock().unlock(); } } return policyInterfaces; } finally { lock.readLock().unlock(); } } /** * Clear entries in the cache based on binding changes. * * @param cacheDescription description of cache to clear * @param cache the cache to clear * @param binding the binding */ private void clearCache(String cacheDescription, Map cache, B binding) { if (binding == null) { lock.writeLock().lock(); try { // A specific binding has not been provided, so clear all entries cache.clear(); if (logger.isDebugEnabled() && cache.isEmpty() == false) logger.debug("Cleared " + cacheDescription + " cache (all class bindings) for policy " + getPolicyClass()); } finally { lock.writeLock().unlock(); } } else { // A specific binding has been provided. Build a list of entries // that require removal. An entry is removed if the binding in the // list is equal or derived from the changed binding. Collection invalidBindings = new ArrayList(); for (B cachedBinding : cache.keySet()) { // Determine if binding is equal or derived from changed binding BehaviourBinding generalisedBinding = cachedBinding; while(generalisedBinding != null) { if (generalisedBinding.equals(binding)) { invalidBindings.add(cachedBinding); break; } generalisedBinding = generalisedBinding.generaliseBinding(); } } // Remove all invalid bindings if (invalidBindings.size() > 0) { lock.writeLock().lock(); try { for (B invalidBinding : invalidBindings) { cache.remove(invalidBinding); if (logger.isDebugEnabled()) logger.debug("Cleared " + cacheDescription + " cache for " + invalidBinding + " and policy " + getPolicyClass()); } } finally { lock.writeLock().unlock(); } } } } }