
org.sakaiproject.memory.impl.SakaiCacheManagerFactoryBean Maven / Gradle / Ivy
/******************************************************************************
* $URL: https://source.sakaiproject.org/svn/master/trunk/header.java $
* $Id: header.java 307632 2014-03-31 15:29:37Z [email protected] $
******************************************************************************
*
* Copyright (c) 2003-2014 The Apereo Foundation
*
* Licensed under the Educational Community 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://opensource.org/licenses/ecl2
*
* 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.sakaiproject.memory.impl;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.config.*;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.component.api.ServerConfigurationService;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.Resource;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
/**
* NOTE: This file was modeled after org/springframework/cache/ehcache/EhCacheManagerFactoryBean.java from URL
* http://grepcode.com/file_/repo1.maven.org/maven2/org.springframework/spring-context-support/3.2.3.RELEASE/org/springframework/cache/ehcache/EhCacheManagerFactoryBean.java/?v=source
*
* @author rlong Bob Long [email protected]
* @author azeckoski Aaron Zeckoski [email protected]
*
* ORIGINAL JAVADOC BELOW:
* {@link FactoryBean} that exposes an EhCache {@link net.sf.ehcache.CacheManager}
* instance (independent or shared), configured from a specified config location.
*
* If no config location is specified, a CacheManager will be configured from
* "ehcache.xml" in the root of the class path (that is, default EhCache initialization
* - as defined in the EhCache docs - will apply).
*
*
Setting up a separate EhCacheManagerFactoryBean is also advisable when using
* EhCacheFactoryBean, as it provides a (by default) independent CacheManager instance
* and cares for proper shutdown of the CacheManager. EhCacheManagerFactoryBean is
* also necessary for loading EhCache configuration from a non-default config location.
*
*
Note: As of Spring 3.0, Spring's EhCache support requires EhCache 1.3 or higher.
* As of Spring 3.2, we recommend using EhCache 2.1 or higher.
*
* @author Dmitriy Kopylenko
* @author Juergen Hoeller
*/
public class SakaiCacheManagerFactoryBean implements FactoryBean, InitializingBean, DisposableBean {
// Check whether EhCache 2.1+ CacheManager.create(Configuration) method is available...
private static final Method createWithConfiguration =
ClassUtils.getMethodIfAvailable(CacheManager.class, "create", Configuration.class);
/** cache defaults **/
private final static String DEFAULT_CACHE_SERVER_URL = "localhost:9510";
private final static int DEFAULT_CACHE_TIMEOUT = 600; // 10 mins
private final static int DEFAULT_CACHE_MAX_OBJECTS = 10000;
protected final Log logger = LogFactory.getLog(getClass());
protected ServerConfigurationService serverConfigurationService;
private Resource configLocation;
private boolean shared = false;
private String cacheManagerName = "Sakai";
private CacheManager cacheManager;
private Boolean cacheEnabled;
public SakaiCacheManagerFactoryBean() {
}
public SakaiCacheManagerFactoryBean(ServerConfigurationService serverConfigurationService) {
this.serverConfigurationService = serverConfigurationService;
this.cacheManagerName = "SakaiTest";
try {
this.afterPropertiesSet();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Set the location of the EhCache config file. A typical value is "/WEB-INF/ehcache.xml".
* Default is "ehcache.xml" in the root of the class path, or if not found,
* "ehcache-failsafe.xml" in the EhCache jar (default EhCache initialization).
* @see net.sf.ehcache.CacheManager#create(java.io.InputStream)
* @see net.sf.ehcache.CacheManager#CacheManager(java.io.InputStream)
*/
public void setConfigLocation(Resource configLocation) {
this.configLocation = configLocation;
}
/**
* Set whether the EhCache CacheManager should be shared (as a singleton at the VM level)
* or independent (typically local within the application). Default is "false", creating
* an independent instance.
* @see net.sf.ehcache.CacheManager#create()
* @see net.sf.ehcache.CacheManager#CacheManager()
*/
public void setShared(boolean shared) {
this.shared = shared;
}
/**
* Set the name of the EhCache CacheManager (if a specific name is desired).
* @see net.sf.ehcache.CacheManager#setName(String)
*/
public void setCacheManagerName(String cacheManagerName) {
this.cacheManagerName = cacheManagerName;
}
/**
* Creates a CacheConfiguration based on the cache name.
* Any Cache properties below that are not set will use the default values
* Valid properties include: maxSize, timeToIdle, timeToLive, eternal
* Defaults: maxSize=10000, timeToIdle=600, timeToLive=600, eternal=false
* Configure cluster caches using: memory.cluster.{cacheName}.{property)={value}
*
* @param clusterCacheName the full name of the cache (e.g. org.sakaiproject.event.impl.ClusterEventTracking.eventsCache)
* @return Terracotta cluster cache configuration
*/
private CacheConfiguration createClusterCacheConfiguration(String clusterCacheName) {
String clusterConfigName = "memory.cluster."+clusterCacheName;
CacheConfiguration clusterCache = new CacheConfiguration(
clusterCacheName,
serverConfigurationService.getInt(clusterConfigName + ".maxEntries", DEFAULT_CACHE_MAX_OBJECTS));
boolean isEternal = serverConfigurationService.getBoolean(clusterConfigName + ".eternal", false);
if (isEternal) {
clusterCache.eternal(true).timeToIdleSeconds(0).timeToLiveSeconds(0);
} else {
clusterCache.eternal(false)
.timeToIdleSeconds(serverConfigurationService.getInt(clusterConfigName + ".timeToIdle", DEFAULT_CACHE_TIMEOUT))
.timeToLiveSeconds(serverConfigurationService.getInt(clusterConfigName + ".timeToLive", DEFAULT_CACHE_TIMEOUT));
}
clusterCache.terracotta(new TerracottaConfiguration()
.nonstop(new NonstopConfiguration()
.timeoutBehavior(new TimeoutBehaviorConfiguration()
.type(TimeoutBehaviorConfiguration.LOCAL_READS_TYPE_NAME))
.enabled(true)));
// Make sure we don't go to local disk
clusterCache.overflowToOffHeap(false);
// Required to control the L2 cache size in terracotta itself, default should be adequate
clusterCache.maxElementsOnDisk(10000);
return clusterCache;
}
/**
* This is the init method
* If using Terracotta, enable caching via sakai.properties and ensure the Terracotta server is reachable
* Use '-Dcom.tc.tc.config.total.timeout=10000' to specify how long we should try to connect to the TC server
*/
public void afterPropertiesSet() throws IOException {
logger.info("Initializing EhCache CacheManager");
InputStream is = (this.configLocation != null ? this.configLocation.getInputStream() : null);
if (this.cacheEnabled == null) {
this.cacheEnabled = serverConfigurationService.getBoolean("memory.cluster.enabled", false);
}
try {
Configuration configuration = (is != null) ? ConfigurationFactory.parseConfiguration(is) : ConfigurationFactory.parseConfiguration();
configuration.setName(this.cacheManagerName);
// force the sizeof calculations to not generate lots of warnings OR degrade server performance
configuration.getSizeOfPolicyConfiguration().maxDepthExceededBehavior(SizeOfPolicyConfiguration.MaxDepthExceededBehavior.ABORT);
configuration.getSizeOfPolicyConfiguration().maxDepth(100);
// Setup the Terracotta cluster config
TerracottaClientConfiguration terracottaConfig = new TerracottaClientConfiguration();
// use Terracotta server if running and available
if (this.cacheEnabled) {
logger.info("Attempting to load cluster caching using Terracotta at: "+
serverConfigurationService.getString("memory.cluster.server.urls", DEFAULT_CACHE_SERVER_URL)+".");
// set the URL to the server
String[] serverUrls = serverConfigurationService.getStrings("memory.cluster.server.urls");
// create comma-separated string of URLs
String serverUrlsString = StringUtils.join(serverUrls, ",");
terracottaConfig.setUrl(serverUrlsString);
terracottaConfig.setRejoin(true);
configuration.addTerracottaConfig(terracottaConfig);
// retrieve the names of all caches that will be managed by Terracotta and create cache configurations for them
String[] caches = serverConfigurationService.getStrings("memory.cluster.names");
if (ArrayUtils.isNotEmpty(caches)) {
for (String cacheName : caches) {
CacheConfiguration cacheConfiguration = this.createClusterCacheConfiguration(cacheName);
if (cacheConfiguration != null) {
configuration.addCache(cacheConfiguration);
}
}
}
// create new cache manager with the above configuration
if (this.shared) {
this.cacheManager = (CacheManager) ReflectionUtils.invokeMethod(createWithConfiguration, null, configuration);
} else {
this.cacheManager = new CacheManager(configuration);
}
} else {
// This block contains the original code from org/springframework/cache/ehcache/EhCacheManagerFactoryBean.java
// A bit convoluted for EhCache 1.x/2.0 compatibility.
// To be much simpler once we require EhCache 2.1+
logger.info("Attempting to load default cluster caching.");
configuration.addTerracottaConfig(terracottaConfig);
if (this.cacheManagerName != null) {
if (this.shared && createWithConfiguration == null) {
// No CacheManager.create(Configuration) method available before EhCache 2.1;
// can only set CacheManager name after creation.
this.cacheManager = (is != null ? CacheManager.create(is) : CacheManager.create());
this.cacheManager.setName(this.cacheManagerName);
} else {
configuration.setName(this.cacheManagerName);
if (this.shared) {
this.cacheManager = (CacheManager) ReflectionUtils.invokeMethod(createWithConfiguration, null, configuration);
} else {
this.cacheManager = new CacheManager(configuration);
}
}
} else if (this.shared) {
// For strict backwards compatibility: use simplest possible constructors...
this.cacheManager = (is != null ? CacheManager.create(is) : CacheManager.create());
} else {
this.cacheManager = (is != null ? new CacheManager(is) : new CacheManager());
}
}
} catch (CacheException ce) {
// this is thrown if we can't connect to the Terracotta server on initialization
if (this.cacheEnabled && this.cacheManager == null) {
logger.error("You have cluster caching enabled in sakai.properties, but do not have a Terracotta server running at "+
serverConfigurationService.getString("memory.cluster.server.urls", DEFAULT_CACHE_SERVER_URL)+
". Please ensure the server is running and available.", ce);
// use the default cache instead
this.cacheEnabled = false;
afterPropertiesSet();
} else {
logger.error("An error occurred while creating the cache manager: ", ce);
}
} finally {
if (is != null) {
is.close();
}
}
}
public CacheManager getObject() {
return this.cacheManager;
}
public Class extends CacheManager> getObjectType() {
return (this.cacheManager != null ? this.cacheManager.getClass() : CacheManager.class);
}
public boolean isSingleton() {
return true;
}
public void destroy() {
logger.info("Shutting down EhCache CacheManager");
this.cacheManager.shutdown();
}
public void setServerConfigurationService(ServerConfigurationService serverConfigurationService) {
this.serverConfigurationService = serverConfigurationService;
}
}