/* * Copyright (C) 1999-2009 Jive Software. All rights reserved. * * Licensed 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 com.jivesoftware.util.cache; import com.tangosol.net.BackingMapManager; import com.tangosol.net.DefaultConfigurableCacheFactory; import com.tangosol.net.MemberListener; import com.tangosol.net.NamedCache; import com.tangosol.net.cache.NearCache; import com.tangosol.net.cache.ReadWriteBackingMap; import com.tangosol.util.*; import org.jivesoftware.util.cache.Cache; import java.util.Collection; import java.util.Comparator; import java.util.Map; import java.util.Set; /** * Clustered implementation of the Cache interface using Tangosol's Coherence product. * */ public class ClusteredCache implements Cache, QueryMap, InvocableMap { /** * The map is used for distributed operations such as get, put, etc. */ protected NamedCache map; /** * The cache is used as the backing store of the main distributed map and * makes decisions about when to cull or expire cache entries. */ private Cache backingCache; /** * Create a new cache. * * @param name a name for the cache, which should be unique per vm. */ protected ClusteredCache(String name) { NamedCache cache = com.tangosol.net.CacheFactory.getCache(name); init(name, cache); } /** * Create a new cache using the supplied named cache as the actual cache implementation * * @param name a name for the cache, which should be unique per vm. * @param cache the cache implementation */ protected ClusteredCache(String name, NamedCache cache) { init(name, cache); } private void init(String name, NamedCache cache) { map = cache; BackingMapManager backingManager = cache.getCacheService().getBackingMapManager(); Map mapBacking = null; if (backingManager instanceof DefaultConfigurableCacheFactory.Manager) { DefaultConfigurableCacheFactory.Manager actualManager = (DefaultConfigurableCacheFactory.Manager) backingManager; int count = 0; mapBacking = actualManager.getBackingMap(name); //this ugly logic is necessary because the backing map instance seems to be made available asynchronously //by the coherence api while (mapBacking == null && count < 5) { // Wait a full second try { Thread.sleep(1000); } catch (Exception e) { /*ignore*/ } count++; mapBacking = actualManager.getBackingMap(name); } if (mapBacking instanceof ReadWriteBackingMap) { ReadWriteBackingMap readWriteMap = (ReadWriteBackingMap)mapBacking; Map realBackingMap = readWriteMap.getInternalCache(); if (realBackingMap instanceof Cache) { backingCache = (Cache)realBackingMap; } } else if (mapBacking instanceof Cache) { backingCache = (Cache)mapBacking; } } if (backingCache == null) { throw new IllegalStateException("Unable to access backing cache for " + name + ". BackingMapManager is a " + backingManager.getClass().getName() + " and backing map is " + ((mapBacking != null) ? mapBacking.getClass().getName() : "null") ); } backingCache.setName(name); } public void addMemberListener(MemberListener listener) { map.getCacheService().addMemberListener(listener); } public void removeMemberListener(MemberListener listener) { map.getCacheService().removeMemberListener(listener); } public void addMapListener(MapListener mapListener, Filter filter, boolean fLite) { map.addMapListener(mapListener, filter, fLite); } public void removeMapListener(MapListener mapListener, Filter filter) { map.removeMapListener(mapListener, filter); } // Cache Interface public String getName() { return backingCache.getName(); } public void setName(String name) { backingCache.setName(name); } public Object put(Object key, Object object) { return map.put(key, object); } public Object get(Object key) { return map.get(key); } public Object remove(Object key) { return map.remove(key); } public void clear() { map.clear(); } public int size() { return backingCache.size(); } public boolean containsKey(Object key) { return map.containsKey(key); } public boolean containsValue(Object value) { return map.containsValue(value); } public Set entrySet() { return map.entrySet(); } public boolean isEmpty() { return map.isEmpty(); } public Set keySet() { return map.keySet(); } public void putAll(Map entries) { map.putAll(entries); } public Collection values() { return map.values(); } public long getCacheHits() { if (map instanceof NearCache) { return ((NearCache)map).getCacheStatistics().getCacheHits(); } else if (backingCache != null) { return backingCache.getCacheHits(); } else { return -1; } } public long getCacheMisses() { if (map instanceof NearCache) { return ((NearCache)map).getCacheStatistics().getCacheMisses(); } else if (backingCache != null) { return backingCache.getCacheMisses(); } else { return -1; } } public int getCacheSize() { return backingCache.getCacheSize(); } public long getMaxCacheSize() { return backingCache.getMaxCacheSize(); } public void setMaxCacheSize(int maxSize) { backingCache.setMaxCacheSize(maxSize); } public long getMaxLifetime() { return backingCache.getMaxLifetime(); } public void setMaxLifetime(long maxLifetime) { backingCache.setMaxLifetime(maxLifetime); } public void destroy() { map.destroy(); } public boolean lock(Object key, long timeout) { return map.lock(key, timeout); } public boolean unlock(Object key) { return map.unlock(key); } ///////// InvocableMap methods ////////////////////////////////// public Object invoke(Object object, EntryProcessor entryProcessor) { return map.invoke(object, entryProcessor); } public Map invokeAll(Collection collection, EntryProcessor entryProcessor) { return map.invokeAll(collection, entryProcessor); } public Map invokeAll(Filter filter, EntryProcessor entryProcessor) { return map.invokeAll(filter, entryProcessor); } public Object aggregate(Collection collection, EntryAggregator entryAggregator) { return map.aggregate(collection, entryAggregator); } public Object aggregate(Filter filter, EntryAggregator entryAggregator) { return map.aggregate(filter, entryAggregator); } ////////////// QueryMap methods ///////////////////////// public Set keySet(Filter filter) { return map.keySet(filter); } public Set entrySet(Filter filter) { return map.entrySet(filter); } public Set entrySet(Filter filter, Comparator comparator) { return map.entrySet(filter, comparator); } public void addIndex(ValueExtractor valueExtractor, boolean sorted, Comparator comparator) { map.addIndex(valueExtractor, sorted, comparator); } public void removeIndex(ValueExtractor valueExtractor) { map.removeIndex(valueExtractor); } }