/*
* Copyright (C) 2005-2010 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
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(); } } } } }