blob: 8606364e9f8343a363f8299c5245dddc4d8b6f54 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.apache.ki.cache.ehcache;
import java.io.IOException;
import java.io.InputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.ki.cache.Cache;
import org.apache.ki.cache.CacheException;
import org.apache.ki.cache.CacheManager;
import org.apache.ki.io.ResourceUtils;
import org.apache.ki.session.mgt.eis.CachingSessionDAO;
import org.apache.ki.util.Destroyable;
import org.apache.ki.util.Initializable;
/**
* Ki <code>CacheManager</code> implementation utilizing the Ehcache framework for all cache functionality.
* <p/>
* This class can {@link #setCacheManager(net.sf.ehcache.CacheManager) accept} a manually configured
* {@link net.sf.ehcache.CacheManager net.sf.ehcache.CacheManager} instance,
* or an <code>ehcache.xml</code> path location can be specified instead and one will be constructed. If neither are
* specified, Ki's failsafe <code><a href="./ehcache.xml">ehcache.xml</a></code> file will be used by default.
*
* <p>This implementation requires EhCache 1.2 and above. Make sure EhCache 1.1 or earlier
* is not in the classpath or it will not work.</p>
*
* <p>Please see the <a href="http://ehcache.sf.net" target="_top">Ehcache website</a> for their documentation.</p>
*
* @author Jeremy Haile
* @author Les Hazlewood
* @see <a href="http://ehcache.sf.net" target="_top">The Ehcache website</a>
* @since 0.2
*/
public class EhCacheManager implements CacheManager, Initializable, Destroyable {
/**
* The default name for the active sessions cache, equal to
* {@link org.apache.ki.session.mgt.eis.CachingSessionDAO#ACTIVE_SESSION_CACHE_NAME CachingSessionDAO.ACTIVE_SESSION_CACHE_NAME}.
*/
public static final String DEFAULT_ACTIVE_SESSIONS_CACHE_NAME = CachingSessionDAO.ACTIVE_SESSION_CACHE_NAME;
/**
* The default maximum number of active sessions in cache <em>memory</em>, equal to <code>20,000</code>.
*/
public static final int DEFAULT_ACTIVE_SESSIONS_CACHE_MAX_ELEM_IN_MEM = 20000;
/**
* The default time the active sessions disk expiration thread will run, equal to <code>600</code> (10 minutes).
*/
public static final int DEFAULT_ACTIVE_SESSIONS_DISK_EXPIRY_THREAD_INTERVAL_SECONDS = 600;
/**
* This class's private log instance.
*/
private static final Logger log = LoggerFactory.getLogger(EhCacheManager.class);
/**
* The EhCache cache manager used by this implementation to create caches.
*/
protected net.sf.ehcache.CacheManager manager;
/**
* Indicates if the CacheManager instance was implicitly/automatically created by this instance, indicating that
* it should be automatically cleaned up as well on shutdown.
*/
private boolean cacheManagerImplicitlyCreated = false;
/**
* Classpath file location of the ehcache CacheManager config file.
*/
private String cacheManagerConfigFile = "classpath:org/ki/cache/ehcache/ehcache.xml";
/**
* Default no argument constructor
*/
public EhCacheManager() {
}
/**
* Returns the wrapped Ehcache {@link net.sf.ehcache.CacheManager CacheManager} instance.
*
* @return the wrapped Ehcache {@link net.sf.ehcache.CacheManager CacheManager} instance.
*/
public net.sf.ehcache.CacheManager getCacheManager() {
return manager;
}
/**
* Sets the wrapped Ehcache {@link net.sf.ehcache.CacheManager CacheManager} instance.
*
* @param manager the wrapped Ehcache {@link net.sf.ehcache.CacheManager CacheManager} instance.
*/
public void setCacheManager(net.sf.ehcache.CacheManager manager) {
this.manager = manager;
}
/**
* Returns the resource location of the config file used to initialize a new
* EhCache CacheManager instance. The string can be any resource path supported by the
* {@link org.apache.ki.io.ResourceUtils#getInputStreamForPath(String)} call.
*
* <p>This property is ignored if the CacheManager instance is injected directly - that is, it is only used to
* lazily create a CacheManager if one is not already provided.</p>
*
* @return the resource location of the config file used to initialize the wrapped
* EhCache CacheManager instance.
*/
public String getCacheManagerConfigFile() {
return this.cacheManagerConfigFile;
}
/**
* Sets the resource location of the config file used to initialize the wrapped
* EhCache CacheManager instance. The string can be any resource path supported by the
* {@link org.apache.ki.io.ResourceUtils#getInputStreamForPath(String)} call.
*
* <p>This property is ignored if the CacheManager instance is injected directly - that is, it is only used to
* lazily create a CacheManager if one is not already provided.</p>
*
* @param classpathLocation resource location of the config file used to create the wrapped
* EhCache CacheManager instance.
*/
public void setCacheManagerConfigFile(String classpathLocation) {
this.cacheManagerConfigFile = classpathLocation;
}
/**
* Acquires the InputStream for the ehcache configuration file using
* {@link ResourceUtils#getInputStreamForPath(String) ResourceUtils.getInputStreamForPath} with the
* path returned from {@link #getCacheManagerConfigFile() getCacheManagerConfigFile()}.
*
* @return the InputStream for the ehcache configuration file.
*/
protected InputStream getCacheManagerConfigFileInputStream() {
String configFile = getCacheManagerConfigFile();
try {
return ResourceUtils.getInputStreamForPath(configFile);
} catch (IOException e) {
throw new IllegalStateException("Unable to obtain input stream for cacheManagerConfigFile.", e);
}
}
/**
* Loads an existing EhCache from the cache manager, or starts a new cache if one is not found.
*
* @param name the name of the cache to load/create.
*/
public final Cache getCache(String name) throws CacheException {
if (log.isTraceEnabled()) {
log.trace("Loading a new EhCache cache named [" + name + "]");
}
try {
net.sf.ehcache.Cache cache = getCacheManager().getCache(name);
if (cache == null) {
if (log.isInfoEnabled()) {
log.info("Could not find a specific ehcache configuration for cache named [" + name + "]; using defaults.");
}
if (name.equals(DEFAULT_ACTIVE_SESSIONS_CACHE_NAME)) {
if (log.isInfoEnabled()) {
log.info("Creating " + DEFAULT_ACTIVE_SESSIONS_CACHE_NAME + " cache with default Ki " +
"session cache settings.");
}
cache = buildDefaultActiveSessionsCache();
manager.addCache(cache);
} else {
manager.addCache(name);
}
cache = manager.getCache(name);
if (log.isInfoEnabled()) {
log.info("Started EHCache named [" + name + "]");
}
} else {
if (log.isInfoEnabled()) {
log.info("Using preconfigured EHCache named [" + cache.getName() + "]");
}
}
return new EhCache(cache);
} catch (net.sf.ehcache.CacheException e) {
throw new CacheException(e);
}
}
/**
* Builds the default cache instance to use for Ki's Session Cache when enterprise Sessions are
* enabled.
*
* @return the default cache instance to use for Ki's Session Cache when enterprise Sessions are
* enabled.
* @throws CacheException if there is a problem constructing the Cache instance.
*/
private net.sf.ehcache.Cache buildDefaultActiveSessionsCache() throws CacheException {
return new net.sf.ehcache.Cache(DEFAULT_ACTIVE_SESSIONS_CACHE_NAME,
DEFAULT_ACTIVE_SESSIONS_CACHE_MAX_ELEM_IN_MEM,
true,
true,
0,
0,
true,
DEFAULT_ACTIVE_SESSIONS_DISK_EXPIRY_THREAD_INTERVAL_SECONDS);
}
/**
* Initializes this instance.
*
* <p>If a {@link #setCacheManager CacheManager} has been
* explicitly set (e.g. via Dependency Injection or programatically) prior to calling this
* method, this method does nothing.</p>
*
* <p>However, if no <tt>CacheManager</tt> has been set, the default Ehcache singleton will be initialized, where
* Ehcache will look for an <tt>ehcache.xml</tt> file at the root of the classpath. If one is not found,
* Ehcache will use its own failsafe configuration file.</p>
*
* <p>Because Ki cannot use the failsafe defaults (failsafe expunges cached objects after 2 minutes,
* something not desireable for Ki sessions), this class manages an internal default configuration for
* this case.</p>
*
* @throws org.apache.ki.cache.CacheException
* if there are any CacheExceptions thrown by EhCache.
* @see net.sf.ehcache.CacheManager#create
*/
public final void init() throws CacheException {
try {
net.sf.ehcache.CacheManager cacheMgr = getCacheManager();
if (cacheMgr == null) {
if (log.isDebugEnabled()) {
log.debug("cacheManager property not set. Constructing CacheManager instance... ");
}
//using the CacheManager constructor, the resulting instance is _not_ a VM singleton
//(as would be the case by calling CacheManager.getInstance(). We do not use the getInstance here
//because we need to know if we need to destroy the CacheManager instance - using the static call,
//we don't know which component is responsible for shutting it down. By using a single EhCacheManager,
//it will always know to shut down the instance if it was responsible for creating it.
cacheMgr = new net.sf.ehcache.CacheManager(getCacheManagerConfigFileInputStream());
if (log.isTraceEnabled()) {
log.trace("instantiated Ehcache CacheManager instance.");
}
cacheManagerImplicitlyCreated = true;
setCacheManager(cacheMgr);
if (log.isDebugEnabled()) {
log.debug("implicit cacheManager created successfully.");
}
}
} catch (Exception e) {
throw new CacheException(e);
}
}
/**
* Shuts-down the wrapped Ehcache CacheManager <b>only if implicitly created</b>.
*
* <p>If another component injected
* a non-null CacheManager into this instace before calling {@link #init() init}, this instance expects that same
* component to also destroy the CacheManager instance, and it will not attempt to do so.
*/
public void destroy() {
if (cacheManagerImplicitlyCreated) {
try {
net.sf.ehcache.CacheManager cacheMgr = getCacheManager();
cacheMgr.shutdown();
} catch (Exception e) {
if (log.isWarnEnabled()) {
log.warn("Unable to cleanly shutdown implicitly created CacheManager instance. " +
"Ignoring (shutting down)...");
}
}
cacheManagerImplicitlyCreated = false;
}
}
}