/*
* 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.EvictedEventNode;
import org.jboss.cache.eviction.EvictionActionPolicy;
import org.jboss.cache.eviction.EvictionAlgorithm;
import org.jboss.cache.eviction.EvictionEvent;
import org.jboss.cache.eviction.EvictionPolicy;
import org.jboss.cache.util.Util;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* Default implementation of a {@link Region}
*
* @author <a href="mailto:manik AT jboss DOT org">Manik Surtani (manik AT jboss DOT org)</a>
*/
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 BlockingQueue<EvictionEvent> 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()
{
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<EvictionEvent> 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) +
"; timerThreadRegistered=" + (evictionAlgorithm != null && regionManager.getEvictionTimerTask().isRegionRegisteredForProcessing(this)) +
"; 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();
}
public void setEvictionRegionConfig(EvictionRegionConfig evictionRegionConfig)
{
this.evictionRegionConfig = evictionRegionConfig;
evictionAlgorithm = createEvictionAlgorithm(evictionRegionConfig.getEvictionAlgorithmConfig(), evictionRegionConfig.getEvictionActionPolicyClassName());
regionManager.getEvictionTimerTask().addRegionToProcess(this);
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.");
}
evictionEventQueue = new LinkedBlockingQueue<EvictionEvent>(size);
}
}
private EvictionAlgorithm createEvictionAlgorithm(EvictionAlgorithmConfig algoConfig, String evictionActionPolicyClass)
{
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)
{
//TODO: Autogenerated. Implement me properly
}
@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();
}
}