/*
* Copyright 2005 Werner Guttmann
*
* 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 org.castor.cache.distributed;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.castor.cache.AbstractBaseCache;
import org.castor.cache.CacheAcquireException;
/**
* EHCache implementation of Castor JDO Cache.
*
* For more details of EHCache, see http://ehcache.sourceforge.net
*
* @see <a href="http://ehcache.sourceforge.net">the EHCache Home Page</a>
* @author <a href="mailto:werner DOT guttmann AT gmx DOT net">Werner Guttmann</a>
* @version $Revision$ $Date: 2006-04-26 00:09:10 +0200 (Mi, 26 Apr 2006) $
* @since 1.0
*/
public final class EHCache extends AbstractBaseCache {
/** The <a href="http://jakarta.apache.org/commons/logging/">Jakarta Commons
* Logging </a> instance used for all logging. */
private static final Log LOG = LogFactory.getLog(EHCache.class);
/** The type of the cache. */
public static final String TYPE = "ehcache";
/** The classname of the implementations factory class. */
public static final String IMPLEMENTATION = "net.sf.ehcache.CacheManager";
/** Parameter types for calling getCache() method on IMPLEMENTATION. */
private static final Class<?>[] TYPES_GET_CACHE = new Class[] {String.class};
/** Parameter types for calling getFromCache() method on cache instance. */
private static final Class<?>[] TYPES_GET = new Class[] {Object.class};
/** Parameter types for calling getFromCache() method on cache instance. */
private static final Class<?>[] TYPES_REMOVE = new Class[] {Object.class};
/** Parameter types for calling constrcutor on 'net.sf.ehcache.Element' class. */
private static final Class<?>[] TYPES_ELEMENT_CONSTRUCTOR =
new Class[] {Object.class, Object.class};
/** The cache instance. */
private Object _cache;
/** The method to invoke on cache instead of calling get() directly. */
private Method _getMethod;
/** The method to invoke on cache instead of calling put() directly. */
private Method _putMethod;
/** The method to invoke on cache instead of calling getSize() directly. */
private Method _getSizeMethod;
/** The method to invoke on cache instead of calling remove() directly. */
private Method _removeMethod;
/** The method to invoke on cache instead of calling removeAll() directly. */
private Method _removeAllMethod;
/** The method to invoke on Element instead of calling getValue() directly. */
private Method _getValueMethod;
/** The method to test whether or not an element is expired. */
private Method _isExpiredMethod;
/** Class instance for 'net.sf.ehcache.Element'. */
private Class<?> _elementClass;
/** Constructor for 'net.sf.ehcache.Element' class. */
private Constructor<?> _elementConstructor;
/**
* {@inheritDoc}
*/
public void initialize(final Properties params) throws CacheAcquireException {
initialize(IMPLEMENTATION, params);
}
/**
* Normally called to initialize FKCache. To be able to test the method without
* having <code>javax.util.jcache.CacheAccessFactory</code> implementation, it
* can also be called with a test implementations classname.
*
* @param implementation Cache implementation classname to initialize.
* @param params Parameters to initialize the cache (e.g. name, capacity).
* @throws CacheAcquireException If cache can not be initialized.
*/
public void initialize(final String implementation, final Properties params)
throws CacheAcquireException {
super.initialize(params);
try {
ClassLoader ldr = this.getClass().getClassLoader();
Class<?> cls = ldr.loadClass(implementation);
invokeStaticMethod(cls, "create", null, null);
Object factory = invokeStaticMethod(cls, "getInstance", null, null);
Boolean cacheExists =
(Boolean) invokeMethod(factory, "cacheExists",
new Class[] {String.class}, new Object[] {getName()});
if (!cacheExists.booleanValue()) {
invokeMethod(factory, "addCache", TYPES_GET_CACHE,
new Object[] {getName()});
}
_cache = invokeMethod(factory, "getCache",
TYPES_GET_CACHE, new Object[] {getName()});
} catch (Exception e) {
String msg = "Error creating EHCache cache: " + e.getMessage();
LOG.error(msg, e);
throw new CacheAcquireException(msg, e);
}
Class<?> cls = _cache.getClass();
try {
_elementClass = Class.forName("net.sf.ehcache.Element");
_getValueMethod = _elementClass.getMethod("getValue", (Class[]) null);
_isExpiredMethod = _elementClass.getMethod("isExpired", (Class[]) null);
} catch (Exception e) {
String msg =
"Failed to instantiate Class for type 'net.sf.ehcache.Element': "
+ e.getMessage();
LOG.error(msg, e);
throw new CacheAcquireException(msg, e);
}
try {
_getSizeMethod = cls.getMethod("getSize", (Class[]) null);
_getMethod = cls.getMethod("get", TYPES_GET);
_putMethod = cls.getMethod("put", new Class[] {_elementClass });
_removeMethod = cls.getMethod("remove", TYPES_REMOVE);
_removeAllMethod = cls.getMethod("removeAll", (Class[]) null);
_elementConstructor =
_elementClass.getConstructor(TYPES_ELEMENT_CONSTRUCTOR);
_removeAllMethod = cls.getMethod("removeAll", (Class[]) null);
} catch (Exception e) {
String msg = "Failed to find method on EHCache instance: " + e.getMessage();
LOG.error(msg, e);
throw new CacheAcquireException(msg, e);
}
}
/**
* {@inheritDoc}
*/
public int size() {
Integer result = new Integer(-1);
try {
result = (Integer) _getSizeMethod.invoke(_cache, (Object[]) null);
} catch (Exception e) {
String msg = "Failed to call method on EHCache instance: " + e.getMessage();
LOG.error(msg, e);
throw new IllegalStateException(e.getMessage());
}
return result.intValue();
}
/**
* {@inheritDoc}
*/
public boolean isEmpty() {
return (size() < 1);
}
/**
* {@inheritDoc}
*/
public boolean containsKey(final Object key) {
return (get(key) != null);
}
/**
* {@inheritDoc}
*/
public boolean containsValue(final Object value) {
throw new UnsupportedOperationException("containsValue(Object)");
}
/**
* {@inheritDoc}
*/
public Object get(final Object key) {
Object result = null;
try {
Object elementInCache = _getMethod.invoke(_cache, new Object[] {key});
if (elementInCache == null) { return null; }
Boolean isExpired = (Boolean) _isExpiredMethod.invoke(elementInCache, (Object[]) null);
if (isExpired.equals(Boolean.FALSE)) {
result = _getValueMethod.invoke(elementInCache, (Object[]) null);
}
} catch (Exception e) {
String msg = "Failed to call method on EHCache instance: " + e.getMessage();
LOG.error(msg, e);
throw new IllegalStateException(e.getMessage());
}
return result;
}
// modification operations of map interface
/**
* {@inheritDoc}
*/
public Object put(final Object key, final Object value) {
Object result = Boolean.FALSE;
try {
result = _elementConstructor.newInstance(new Object[] {key, value});
_putMethod.invoke(_cache, new Object[] {result});
} catch (Exception e) {
String msg = "Failed to call method on EHCache instance: " + e.getMessage();
LOG.error(msg, e);
throw new IllegalStateException(e.getMessage());
}
return result;
}
/**
* {@inheritDoc}
*/
public Object remove(final Object key) {
Object oldValue = get(key);
try {
_removeMethod.invoke(_cache, new Object[] {String.valueOf(key)});
} catch (Exception e) {
String msg = "Failed to call method on EHCache instance: " + e.getMessage();
LOG.error(msg, e);
throw new IllegalStateException(e.getMessage());
}
return oldValue;
}
// bulk operations of map interface
/**
* {@inheritDoc}
*/
public void putAll(final Map<? extends Object, ? extends Object> map) {
Iterator<? extends Entry<? extends Object, ? extends Object>> iter;
iter = map.entrySet().iterator();
while (iter.hasNext()) {
Entry<? extends Object, ? extends Object> entry = iter.next();
String key = String.valueOf(entry.getKey());
put (key, entry.getValue());
}
}
/**
* {@inheritDoc}
*/
public void clear() {
try {
_removeAllMethod.invoke(_cache, (Object[]) null);
} catch (Exception e) {
String msg = "Failed to call method on EHCache instance: " + e.getMessage();
LOG.error(msg, e);
throw new IllegalStateException(e.getMessage());
}
}
// view operations of map interface
/**
* {@inheritDoc}
*/
public Set<Object> keySet() {
throw new UnsupportedOperationException("keySet()");
}
/**
* {@inheritDoc}
*/
public Collection<Object> values() {
throw new UnsupportedOperationException("values()");
}
/**
* {@inheritDoc}
*/
public Set<Entry<Object, Object>> entrySet() {
throw new UnsupportedOperationException("entrySet()");
}
/**
* Normally called to shutdown CoherenceCache. To be able to test the method
* without having <code>com.tangosol.net.CacheFactory</code> implementation,
* it can also be called with a test implementations classname.
*
* @param implementation Cache implementation classname to shutdown.
*/
public void shutdown(final String implementation) {
try {
ClassLoader ldr = this.getClass().getClassLoader();
Class<?> cls = ldr.loadClass(implementation);
if (cls != null) {
Object factory = invokeStaticMethod(cls, "getInstance", null, null);
Method method = cls.getMethod("shutdown", new Class[] {cls});
method.invoke(factory, (Object[]) null);
}
} catch (Exception e) {
LOG.error("Problem shutting down Coherence cluster member", e);
}
}
/**
* {@inheritDoc}
*/
public String getType() { return TYPE; }
}