/* * JBoss, Home of Professional Open Source. * Copyright 2000 - 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This 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 2.1 of * the License, or (at your option) any later version. * * This software 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 this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.annotations.Compat; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionAlgorithmConfig; import org.jboss.cache.config.EvictionPolicyConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.eviction.*; import org.jboss.cache.util.Util; import java.util.HashMap; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; /** * Default implementation of a {@link Region} * * @author Manik Surtani (manik AT jboss DOT org) */ public class RegionImpl implements Region { private static final Log log = LogFactory.getLog(RegionImpl.class); private static final boolean trace = log.isTraceEnabled(); private final RegionManager regionManager; private Fqn fqn; private Status status; private ClassLoader classLoader; private volatile BlockingQueue evictionEventQueue = null; private int capacityWarnThreshold = 0; private EvictionRegionConfig evictionRegionConfig; private EvictionAlgorithm evictionAlgorithm; /** * Constructs a marshalling region from an fqn and region manager. */ public RegionImpl(Fqn fqn, RegionManager regionManager) { this.fqn = fqn; this.regionManager = regionManager; status = !regionManager.isDefaultInactive() ? Status.ACTIVE : Status.INACTIVE; } /** * Constructs an eviction region from a policy and configuration, defined by an fqn and region manager. */ public RegionImpl(EvictionRegionConfig config, Fqn fqn, RegionManager regionManager) { this(fqn, regionManager); this.evictionRegionConfig = config; createQueue(); } public Configuration getCacheConfiguration() { if (regionManager != null && regionManager.getCache() != null) return regionManager.getCache().getConfiguration(); else return null; } public void registerContextClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } public void unregisterContextClassLoader() { this.classLoader = null; } public void activate() { regionManager.activate(fqn); status = Status.ACTIVE; } public void activateIfEmpty() { regionManager.activateIfEmpty(fqn); status = Status.ACTIVE; } public void deactivate() { regionManager.deactivate(fqn); status = Status.INACTIVE; } public boolean isActive() { return status == Status.ACTIVE; } public ClassLoader getClassLoader() { return classLoader; } public void processEvictionQueues() { if (trace) log.trace("Processing eviction queue for region ["+getFqn()+"]. Queue size is " + evictionEventQueue.size()); evictionAlgorithm.process(evictionEventQueue); } public Fqn getFqn() { return fqn; } public void setStatus(Status status) { this.status = status; } public Status getStatus() { return status; } public void setActive(boolean b) { status = b ? Status.ACTIVE : Status.INACTIVE; } // -------- eviction stuff ----- public BlockingQueue getEvictionEventQueue() { return evictionEventQueue; } public void markNodeCurrentlyInUse(Fqn fqn, long timeout) { registerEvictionEvent(fqn, EvictionEvent.Type.MARK_IN_USE_EVENT, 0).setInUseTimeout(timeout); } public void unmarkNodeCurrentlyInUse(Fqn fqn) { registerEvictionEvent(fqn, EvictionEvent.Type.UNMARK_USE_EVENT, 0); } @Override public String toString() { return "RegionImpl{" + "fqn=" + fqn + "; classloader=" + classLoader + "; status=" + status + "; eviction=" + (evictionAlgorithm != null) + "; evictionQueueSize=" + (evictionAlgorithm == null ? "-1" : evictionEventQueue.size()) + '}'; } public int compareTo(Region other) { return getFqn().compareTo(other.getFqn()); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; RegionImpl region = (RegionImpl) o; if (fqn != null ? !fqn.equals(region.fqn) : region.fqn != null) return false; return true; } @Override public int hashCode() { return (fqn != null ? fqn.hashCode() : 0); } public void resetEvictionQueues() { evictionEventQueue.clear(); evictionAlgorithm.getEvictionQueue().clear(); } public void setEvictionRegionConfig(EvictionRegionConfig evictionRegionConfig) { if (trace) log.trace("setEvictionRegionConfig called with " + evictionRegionConfig); this.evictionRegionConfig = evictionRegionConfig; evictionAlgorithm = createEvictionAlgorithm(evictionRegionConfig.getEvictionAlgorithmConfig(), evictionRegionConfig.getEvictionActionPolicyClassName()); if (evictionEventQueue == null) createQueue(); evictionAlgorithm.initialize(); } public EvictionRegionConfig getEvictionRegionConfig() { return evictionRegionConfig; } public EvictionEvent registerEvictionEvent(Fqn fqn, EvictionEvent.Type eventType) { return registerEvictionEvent(fqn, eventType, 0); } public EvictionEvent registerEvictionEvent(Fqn fqn, EvictionEvent.Type eventType, int elementDifference) { if (evictionAlgorithm.canIgnoreEvent(eventType)) return null; EvictionEvent event = new EvictionEvent(fqn, eventType, elementDifference); registerEvictionEvent(event); return event; } private void registerEvictionEvent(EvictionEvent ee) { try { if (evictionEventQueue == null) createQueue();// in case the queue does not exist yet. if (evictionEventQueue.size() > capacityWarnThreshold && log.isWarnEnabled()) { log.warn("putNodeEvent(): eviction node event queue size is at 98% threshold value of capacity: " + evictionRegionConfig.getEventQueueSize() + " Region: " + fqn + " You will need to reduce the wakeUpIntervalSeconds parameter."); } evictionEventQueue.put(ee); } catch (InterruptedException e) { if (log.isDebugEnabled()) log.debug("Interrupted on adding event", e); // reinstate interrupt flag Thread.currentThread().interrupt(); } } private void createQueue() { if (evictionEventQueue == null) { if (evictionRegionConfig == null) { throw new IllegalArgumentException("null eviction configuration"); } int size = evictionRegionConfig.getEventQueueSize(); capacityWarnThreshold = (98 * size) / 100 - 100; if (capacityWarnThreshold <= 0 && log.isWarnEnabled()) { log.warn("Capacity warn threshold used in eviction is smaller than 1. Defined Event queu size is:" + size); } synchronized (this) { if (evictionEventQueue == null) evictionEventQueue = new LinkedBlockingQueue(size); } } } private EvictionAlgorithm createEvictionAlgorithm(EvictionAlgorithmConfig algoConfig, String evictionActionPolicyClass) { if (trace) log.trace("Creating eviction algorithm using config " + algoConfig); if (algoConfig == null) throw new IllegalArgumentException("Eviction algorithm class must not be null!"); if (evictionActionPolicyClass == null) throw new IllegalArgumentException("Eviction action policy class must not be null!"); try { if (trace) log.trace("Instantiating " + evictionActionPolicyClass); EvictionActionPolicy actionPolicy = (EvictionActionPolicy) Util.getInstance(evictionActionPolicyClass); actionPolicy.setCache(regionManager.getCache()); if (trace) log.trace("Instantiating " + algoConfig.getEvictionAlgorithmClassName()); EvictionAlgorithm algorithm = (EvictionAlgorithm) Util.getInstance(algoConfig.getEvictionAlgorithmClassName()); algorithm.setEvictionActionPolicy(actionPolicy); algorithm.assignToRegion(fqn, regionManager.getCache(), algoConfig, regionManager.getConfiguration()); return algorithm; } catch (Exception e) { log.fatal("Unable to instantiate eviction algorithm " + algoConfig.getEvictionAlgorithmClassName(), e); throw new IllegalStateException(e); } } public Region copy(Fqn newRoot) { RegionImpl clone; clone = new RegionImpl(evictionRegionConfig, Fqn.fromRelativeFqn(newRoot, fqn), regionManager); clone.status = status; // we also need to copy all of the eviction event nodes to the clone's queue clone.createQueue(); for (EvictionEvent een : this.evictionEventQueue) { clone.registerEvictionEvent(een.getFqn(), een.getEventType(), een.getElementDifference()); } return clone; } @Deprecated @Compat @SuppressWarnings("deprecation") public void setEvictionPolicy(EvictionPolicyConfig evictionPolicyConfig) { try { // need to create an EvictionRegionConfig from this. EvictionRegionConfig erc = new EvictionRegionConfig(getFqn()); String epClassName = evictionPolicyConfig.getEvictionPolicyClass(); EvictionAlgorithmConfig eac = getEvictionAlgorithmConfig(epClassName); erc.setEvictionAlgorithmConfig(eac); if (!erc.isDefaultRegion()) { erc.setDefaults(regionManager.getConfiguration().getEvictionConfig().getDefaultEvictionRegionConfig()); } setEvictionRegionConfig(erc); } catch (Exception e) { throw new CacheException(e); } } /** * This is to provide backward compatibility and is not guaranteed to work for every type of eviction policy out * there, particularly custom ones. * * @param evictionPolicyClass * @return An eviction algorithm config. */ @Deprecated private EvictionAlgorithmConfig getEvictionAlgorithmConfig(String evictionPolicyClass) throws Exception { Map> legacyConfigMap = new HashMap>(); legacyConfigMap.put(FIFOPolicy.class.getName(), FIFOAlgorithmConfig.class); legacyConfigMap.put(LRUPolicy.class.getName(), LRUAlgorithmConfig.class); legacyConfigMap.put(LFUPolicy.class.getName(), LFUAlgorithmConfig.class); legacyConfigMap.put(MRUPolicy.class.getName(), MRUAlgorithmConfig.class); legacyConfigMap.put(ExpirationPolicy.class.getName(), ExpirationAlgorithmConfig.class); legacyConfigMap.put(ElementSizePolicy.class.getName(), ElementSizeAlgorithmConfig.class); legacyConfigMap.put(NullEvictionPolicy.class.getName(), NullEvictionAlgorithmConfig.class); Class c = legacyConfigMap.get(evictionPolicyClass); if (c != null) { // this is one of our "shipped" policies. Easy mapping! return Util.getInstance(c); } else { // tougher. This is a custom policy. // lets default to LRUPolicy first... final EvictionPolicy ep = (EvictionPolicy) Util.getInstance(evictionPolicyClass); ep.getEvictionAlgorithm(); return new LRUAlgorithmConfig() { @Override public String getEvictionAlgorithmClassName() { return ep.getEvictionAlgorithm().getClass().getName(); } }; } } @Deprecated @Compat @SuppressWarnings("deprecation") public EvictionPolicyConfig getEvictionPolicyConfig() { return null; //TODO: Autogenerated. Implement me properly } @Deprecated @Compat @SuppressWarnings("deprecation") public EvictionPolicy getEvictionPolicy() { return null; //TODO: Autogenerated. Implement me properly } @Deprecated @Compat public int nodeEventQueueSize() { BlockingQueue q = getEvictionEventQueue(); return q == null ? 0 : q.size(); } @Compat @Deprecated @SuppressWarnings("deprecation") public EvictedEventNode takeLastEventNode() { try { EvictionEvent ee = getEvictionEventQueue().poll(0, TimeUnit.SECONDS); if (ee instanceof EvictedEventNode) return (EvictedEventNode) ee; else return new EvictedEventNode(ee); } catch (InterruptedException e) { log.debug("trace", e); } return null; } @Deprecated @Compat @SuppressWarnings("deprecation") public void putNodeEvent(EvictedEventNode event) { this.registerEvictionEvent(event); } @Deprecated @Compat public Region clone() throws CloneNotSupportedException { return (Region) super.clone(); } }