/*
* Copyright (c) 2005-2009, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. 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.wso2.carbon.caching.infinispan;
import net.sf.jsr107cache.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.infinispan.AdvancedCache;
import org.infinispan.config.Configuration;
import org.infinispan.config.GlobalConfiguration;
import org.infinispan.container.entries.InternalCacheEntry;
import org.infinispan.interceptors.CacheMgmtInterceptor;
import org.infinispan.interceptors.base.CommandInterceptor;
import org.infinispan.lifecycle.ComponentStatus;
import org.infinispan.manager.DefaultCacheManager;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.stats.Stats;
import org.wso2.carbon.base.CarbonBaseUtils;
import org.wso2.carbon.caching.core.CacheConfiguration;
import org.wso2.carbon.caching.core.CarbonCacheManager;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* A cache manager based on <a href="http://jboss.org/infinispan">Infinispan</a> from JBoss.
*/
@SuppressWarnings("unused")
public class InfinispanCacheManager extends CacheManager implements CarbonCacheManager {
private EmbeddedCacheManager cacheManager;
private static final long DEFAULT_CACHE_LIFESPAN = 60000L;
private static final String DEFAULT_CLUSTER_NAME = "wso2carbon-cache";
private static final List<String> ALLOWED_CLASSES =
Arrays.asList("org.wso2.carbon.utils.multitenancy.CarbonContextHolder");
private final Map<String, Cache> caches = Collections.synchronizedMap(
new HashMap<String, Cache>());
private static final Log log = LogFactory.getLog(InfinispanCacheManager.class);
/**
* {@inheritDoc}
*/
public void initialize(String carbonHome) {
CarbonBaseUtils.checkSecurity(ALLOWED_CLASSES);
log.debug("Starting Cache Manager initialization");
CacheConfiguration cacheConfiguration = CacheConfiguration.getInstance();
// Infinispan requires the bind.address system property to be set to function as a
// distributed cache. This has been done in CarbonServerManager#initializeCarbon()
GlobalConfiguration globalConfiguration;
if (Boolean.toString(true).equals(
cacheConfiguration.getProperty("configuration.clustering.enabled"))) {
globalConfiguration = GlobalConfiguration.getClusteredDefault();
String clusterName =
cacheConfiguration.getProperty("configuration.clustering.clusterName");
if (clusterName != null) {
globalConfiguration.setClusterName(clusterName);
} else {
globalConfiguration.setClusterName(DEFAULT_CLUSTER_NAME);
}
} else {
globalConfiguration = GlobalConfiguration.getNonClusteredDefault();
}
String ec2ConfigFile = cacheConfiguration.getProperty("configuration.ec2.configFile");
if (ec2ConfigFile != null) {
Properties props = new Properties();
props.setProperty("configurationFile", ec2ConfigFile.replace("${carbon.home}",
carbonHome));
globalConfiguration.setTransportProperties(props);
}
globalConfiguration.setAllowDuplicateDomains(true);
Configuration configuration = new Configuration();
String cacheMode = cacheConfiguration.getProperty("configuration.cacheMode");
boolean sync = Boolean.toString(true).equals(
cacheConfiguration.getProperty("configuration.sync"));
if ("distributed".equals(cacheMode)) {
configuration.setCacheMode(sync ?
Configuration.CacheMode.DIST_SYNC : Configuration.CacheMode.DIST_ASYNC);
if (log.isDebugEnabled()) {
log.debug("Infinispan Cache Mode : " + cacheMode +
(sync ? " (synchronous)" : " (asynchronous)"));
}
if (Boolean.toString(true).equals(
cacheConfiguration.getProperty("configuration.l1.enabled"))) {
configuration.setL1CacheEnabled(true);
String l1Lifespan = cacheConfiguration.getProperty("configuration.l1.lifespan");
if (l1Lifespan != null) {
configuration.setL1Lifespan(Long.parseLong(l1Lifespan));
if (log.isDebugEnabled()) {
log.debug("Infinispan L1 Cache : enabled");
log.debug("Infinispan L1 Lifespan : " + l1Lifespan);
}
} else {
configuration.setL1Lifespan(DEFAULT_CACHE_LIFESPAN);
if (log.isDebugEnabled()) {
log.debug("Infinispan L1 Cache : enabled");
log.debug("Infinispan L1 Lifespan : " + DEFAULT_CACHE_LIFESPAN);
}
}
} else if (log.isDebugEnabled()) {
log.debug("Infinispan L1 Cache : disabled");
}
} else if ("replicated".equals(cacheMode)) {
configuration.setCacheMode(sync ?
Configuration.CacheMode.REPL_SYNC : Configuration.CacheMode.REPL_ASYNC);
if (log.isDebugEnabled()) {
log.debug("Infinispan Cache Mode : " + cacheMode +
(sync ? " (synchronous)" : " (asynchronous)"));
}
} else if ("invalidation".equals(cacheMode)) {
configuration.setCacheMode(sync ?
Configuration.CacheMode.INVALIDATION_SYNC :
Configuration.CacheMode.INVALIDATION_ASYNC);
if (log.isDebugEnabled()) {
log.debug("Infinispan Cache Mode : " + cacheMode +
(sync ? " (synchronous)" : " (asynchronous)"));
}
} else {
configuration.setCacheMode(Configuration.CacheMode.LOCAL);
if (log.isDebugEnabled()) {
log.debug("Infinispan Cache Mode : local");
}
}
configuration.setExposeJmxStatistics(true);
cacheManager = new DefaultCacheManager(globalConfiguration, configuration);
log.debug("Successfully Initialized Infinispan Cache Manager");
}
/**
* {@inheritDoc}
*/
public String getDefaultCacheName() {
return DefaultCacheManager.DEFAULT_CACHE_NAME;
}
/**
* {@inheritDoc}
*/
public Cache getCache(String cacheName) {
Cache cache = caches.get(cacheName);
if (cache == null) {
// We wrap each Infinispan Cache, so that it can be used via the standard JSR107
// JCache API.
ClassLoader tccl = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(InfinispanCacheManager.class.getClassLoader());
cache = new InfinispanJCacheWrapper(cacheManager.getCache(cacheName));
registerCache(cacheName, cache);
} finally {
Thread.currentThread().setContextClassLoader(tccl);
}
}
return cache;
}
/**
* {@inheritDoc}
*/
public void registerCache(String cacheName, Cache cache) {
if (cache == null) {
Cache removedCache = caches.remove(cacheName);
// Removing the cache will also result in the stopping of the cache
if (removedCache != null && removedCache instanceof InfinispanJCacheWrapper) {
org.infinispan.Cache original =
((InfinispanJCacheWrapper) removedCache).getOriginal();
if (original.getStatus() == ComponentStatus.RUNNING) {
original.stop();
}
}
} else {
// We register a cache only if there is no cache already registered.
if (caches.get(cacheName) == null) {
caches.put(cacheName, cache);
}
}
}
/**
* {@inheritDoc}
*/
public CacheFactory getCacheFactory() throws CacheException {
throw new CacheException("The InfinispanCacheManager does not provide an " +
"in-built nor look-up a CacheFactory.");
}
////////////////////////////////////////////////////////
// JSR107 JCache wrapper for Infinispan
////////////////////////////////////////////////////////
private static class InfinispanJCacheEntry implements CacheEntry {
private InternalCacheEntry cacheEntry;
private InfinispanJCacheEntry(InternalCacheEntry cacheEntry) {
this.cacheEntry = cacheEntry;
}
public int getHits() {
throw new UnsupportedOperationException("Infinispan does not allow to find the" +
" number of hits on a per-entry basis.");
}
public long getLastAccessTime() {
if (cacheEntry == null) {
return 0;
}
return cacheEntry.getLastUsed();
}
public long getLastUpdateTime() {
if (cacheEntry == null) {
return 0;
}
return cacheEntry.getLastUsed();
}
public long getCreationTime() {
if (cacheEntry == null) {
return 0;
}
return cacheEntry.getCreated();
}
public long getExpirationTime() {
if (cacheEntry == null) {
return 0;
}
return cacheEntry.getExpiryTime();
}
public long getVersion() {
// We use the time at which the cache entry was created as the version of the entry.
return cacheEntry.getCreated();
}
public boolean isValid() {
return cacheEntry.isValid();
}
public long getCost() {
// This implementation does not have a notion of cost. Accordingly, 0 is always
// returned.
return 0;
}
public Object getKey() {
return cacheEntry != null ? cacheEntry.getKey() : null;
}
public Object getValue() {
return cacheEntry != null ? cacheEntry.getValue() : null;
}
public Object setValue(Object o) {
return cacheEntry != null ? cacheEntry.setValue(o) : null;
}
}
private static class InfinispanJCacheStatistics implements CacheStatistics {
private Stats stats;
private CacheMgmtInterceptor interceptor;
private InfinispanJCacheStatistics(AdvancedCache cache) {
this.stats = cache.getStats();
this.interceptor = null;
try {
@SuppressWarnings("unchecked")
List<CommandInterceptor> chain = (List<CommandInterceptor>) cache.getInterceptorChain();
for (CommandInterceptor i : chain) {
if (i instanceof CacheMgmtInterceptor) {
interceptor = (CacheMgmtInterceptor)i;
break;
}
}
} catch (Exception ignored) {
// We are not bothered if this fails.
}
}
public int getStatisticsAccuracy() {
throw new UnsupportedOperationException("This version of Infinispan does not provide " +
"details on accuracy of statistics.");
}
public int getObjectCount() {
return stats.getCurrentNumberOfEntries();
}
public int getCacheHits() {
return (int) stats.getHits();
}
public int getCacheMisses() {
return (int) stats.getMisses();
}
public void clearStatistics() {
if (interceptor != null) {
interceptor.resetStatistics();
}
}
}
private static class InfinispanJCacheWrapper implements Cache {
private org.infinispan.Cache cache;
public InfinispanJCacheWrapper(org.infinispan.Cache cache) {
if (cache.getStatus() == ComponentStatus.TERMINATED ||
cache.getStatus() == ComponentStatus.INSTANTIATED) {
cache.start();
} else if (cache.getStatus() == ComponentStatus.FAILED) {
throw new RuntimeException("Failed to start the cache: " + cache.getName());
}
this.cache = cache;
}
public org.infinispan.Cache getOriginal() {
return cache;
}
public boolean containsKey(Object o) {
return cache.containsKey(o);
}
public boolean containsValue(Object o) {
return cache.containsValue(o);
}
public Set entrySet() {
return cache.entrySet();
}
public boolean equals(Object o) {
return (o instanceof InfinispanJCacheWrapper) &&
((InfinispanJCacheWrapper)o).cache.equals(cache);
}
public int hashCode() {
return (int) (((long) cache.hashCode()) +
this.getClass().getCanonicalName().hashCode());
}
public boolean isEmpty() {
return cache.isEmpty();
}
public Set keySet() {
return cache.keySet();
}
@SuppressWarnings("unchecked")
public void putAll(Map map) {
cache.putAll(map);
}
public int size() {
return cache.size();
}
public Collection values() {
return cache.values();
}
public Object get(Object o) {
return cache.get(o);
}
public Map getAll(Collection collection) throws CacheException {
Map<Object, Object> output = new ConcurrentHashMap<Object, Object>();
for (Object o : collection) {
output.put(o, get(o));
}
return output;
}
public void load(Object o) throws CacheException {
for (Object listener : cache.getListeners()) {
if (listener instanceof CacheListener) {
((CacheListener)listener).onLoad(o);
}
}
}
public void loadAll(Collection collection) throws CacheException {
for (Object o : collection) {
load(o);
}
}
public Object peek(Object o) {
return cache.getAdvancedCache().get(o);
}
@SuppressWarnings("unchecked")
public Object put(Object o, Object o1) {
return cache.put(o, o1);
}
public CacheEntry getCacheEntry(Object o) {
return new InfinispanJCacheEntry(
cache.getAdvancedCache().getDataContainer().get(o));
}
public CacheStatistics getCacheStatistics() {
return new InfinispanJCacheStatistics(cache.getAdvancedCache());
}
public Object remove(Object o) {
return cache.remove(o);
}
public void clear() {
cache.clear();
}
public void evict() {
cache.getAdvancedCache().getEvictionManager().processEviction();
}
public void addListener(CacheListener cacheListener) {
cache.addListener(cacheListener);
}
public void removeListener(CacheListener cacheListener) {
cache.removeListener(cacheListener);
}
}
}