/*
* Created on Nov 10, 2004
*
* 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.
*
* Copyright @2007 the original author or authors.
*/
package org.springmodules.cache.provider;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.StringUtils;
import org.springmodules.cache.CacheException;
import org.springmodules.cache.CachingModel;
import org.springmodules.cache.FatalCacheException;
import org.springmodules.cache.FlushingModel;
import org.springmodules.cache.serializable.SerializableFactory;
import java.io.Serializable;
/**
* Template for implementations of <code>{@link CacheProviderFacade}</code>.
*
* @author Omar Irbouh
* @author Alex Ruiz
*/
public abstract class AbstractCacheProviderFacade implements
CacheProviderFacade, InitializingBean {
/**
* Logger available to subclasses
*/
protected final Log logger = LogFactory.getLog(getClass());
private boolean failQuietlyEnabled;
private SerializableFactory serializableFactory;
/**
* Validates the properties of this class after being set by the
* <code>BeanFactory</code>.
*
* @throws FatalCacheException if one or more properties of this facade are in an illegal state
* @see #validateCacheManager()
*/
public final void afterPropertiesSet() throws FatalCacheException {
validateCacheManager();
onAfterPropertiesSet();
}
/**
* @see CacheProviderFacade#cancelCacheUpdate(Serializable)
*/
public final void cancelCacheUpdate(Serializable key) throws CacheException {
if (logger.isDebugEnabled()) {
logger.debug("Attempt to cancel a cache update using the key <"
+ StringUtils.quoteIfString(key) + ">");
}
try {
onCancelCacheUpdate(key);
} catch (CacheException exception) {
handleCatchedException(exception);
}
}
/**
* @see CacheProviderFacade#flushCache(FlushingModel)
*/
public final void flushCache(FlushingModel model) throws CacheException {
if (logger.isDebugEnabled()) {
logger.debug("Attempt to flush the cache using model <" + model + ">");
}
if (model != null) {
try {
onFlushCache(model);
logger.debug("Cache has been flushed.");
} catch (CacheException exception) {
handleCatchedException(exception);
}
}
}
/**
* @see CacheProviderFacade#getFromCache(Serializable,CachingModel)
*/
public final Object getFromCache(Serializable key, CachingModel model)
throws CacheException {
if (logger.isDebugEnabled()) {
logger.debug("Attempt to retrieve a cache entry using key <"
+ StringUtils.quoteIfString(key) + "> and cache model <" + model
+ ">");
}
Object cachedObject = null;
try {
if (model != null) {
cachedObject = onGetFromCache(key, model);
// deserialize the value if required
if (cachedObject != null) {
cachedObject = deserializeValueIfNecessary(cachedObject);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Retrieved cache element <"
+ StringUtils.quoteIfString(cachedObject) + ">");
}
} catch (CacheException exception) {
handleCatchedException(exception);
}
return cachedObject;
}
/**
* @see CacheProviderFacade#isFailQuietlyEnabled()
*/
public final boolean isFailQuietlyEnabled() {
return failQuietlyEnabled;
}
/**
* @see CacheProviderFacade#putInCache(Serializable,CachingModel,Object)
* @see #makeSerializableIfNecessary(Object)
*/
public final void putInCache(Serializable key, CachingModel model, Object obj)
throws CacheException {
if (logger.isDebugEnabled()) {
logger.debug("Attempt to store the object <" + obj
+ "> in the cache using key <" + StringUtils.quoteIfString(key)
+ "> and model <" + model + ">");
}
try {
Object newCacheElement = makeSerializableIfNecessary(obj);
if (model != null) {
onPutInCache(key, model, newCacheElement);
logger.debug("Object was successfully stored in the cache");
}
} catch (CacheException exception) {
handleCatchedException(exception);
}
}
/**
* @see CacheProviderFacade#removeFromCache(Serializable,CachingModel)
*/
public final void removeFromCache(Serializable key, CachingModel model)
throws CacheException {
if (logger.isDebugEnabled()) {
logger.debug("Attempt to remove an entry from the cache using key <"
+ StringUtils.quoteIfString(key) + "> and model <" + model + ">");
}
if (model != null) {
try {
onRemoveFromCache(key, model);
logger.debug("Object removed from the cache");
} catch (CacheException exception) {
handleCatchedException(exception);
}
}
}
/**
* Sets the flag that indicates if any exception thrown at run-time by the
* cache manager should be propagated (<code>false</code>) or not (<code>true</code>.)
*
* @param newFailQuietlyEnabled the new value for the flag
*/
public final void setFailQuietlyEnabled(boolean newFailQuietlyEnabled) {
failQuietlyEnabled = newFailQuietlyEnabled;
}
/**
* Sets the factory that makes serializable the objects to be stored in the
* cache (if the cache requires serializable elements).
*
* @param newSerializableFactory the new factory of serializable objects
*/
public final void setSerializableFactory(
SerializableFactory newSerializableFactory) {
serializableFactory = newSerializableFactory;
}
/**
* Asserts that the given cache manager is not <code>null</code>.
*
* @param cacheManager the cache manager to check
* @throws FatalCacheException if the cache manager is <code>null</code>
*/
protected final void assertCacheManagerIsNotNull(Object cacheManager)
throws FatalCacheException {
if (cacheManager == null) {
throw new FatalCacheException("The cache manager should not be null");
}
}
/**
* Rethrows the given exception if "fail quietly" is enabled.
*
* @param exception the catched exception to be potentially rethrown.
* @throws CacheException if this cache provider has not been configured to "fail quietly."
*/
protected final void handleCatchedException(CacheException exception)
throws CacheException {
logger.error(exception.getMessage(), exception);
if (!isFailQuietlyEnabled()) {
throw exception;
}
}
/**
* @return <code>true</code> if the cache used by this facade can only store
* serializable objects.
*/
protected abstract boolean isSerializableCacheElementRequired();
/**
* Makes the given object serializable if:
* <ul>
* <li>The cache can only store serializable objects</li>
* <li>The given object does not implement <code>java.io.Serializable</code>
* </li>
* </ul>
* Otherwise, will return the same object passed as argument.
*
* @param obj the object to check.
* @return the given object as a serializable object if necessary.
* @throws ObjectCannotBeCachedException if the cache requires serializable elements, the given object
* does not implement <code>java.io.Serializable</code> and the
* factory of serializable objects is <code>null</code>.
* @see #setSerializableFactory(SerializableFactory)
* @see org.springmodules.cache.serializable.SerializableFactory#makeSerializableIfNecessary(Object)
*/
protected final Object makeSerializableIfNecessary(Object obj) {
if (!isSerializableCacheElementRequired()) {
return obj;
}
if (obj instanceof Serializable) {
return obj;
}
if (serializableFactory != null) {
return serializableFactory.makeSerializableIfNecessary(obj);
}
throw new ObjectCannotBeCachedException(
"The cache can only store implementations of java.io.Serializable");
}
/**
* Deserialize the value from the given object if:
* <ul>
* <li>The cache can only store serializable objects</li>
* <li>The given object does not implement <code>java.io.Serializable</code>
* </li>
* </ul>
* Otherwise, will throw an {@link InvalidObjectInCacheException}.
*
* @param obj the object to check.
* @return the given object as a serializable object if necessary.
* @throws InvalidObjectInCacheException if the cache requires serializable elements, the given object
* does not implement <code>java.io.Serializable</code> and the
* factory of serializable objects is <code>null</code>.
* @see #setSerializableFactory(SerializableFactory)
* @see org.springmodules.cache.serializable.SerializableFactory#getOriginalValue(Object)
*/
protected final Object deserializeValueIfNecessary(Object obj) {
if (!isSerializableCacheElementRequired()) {
return obj;
}
if (obj instanceof Serializable) {
if (serializableFactory != null) {
return serializableFactory.getOriginalValue(obj);
}
return obj;
}
throw new InvalidObjectInCacheException(
"The object retrieved from the cache is not of the required " +
"type: java.io.Serializable.");
}
/**
* Gives subclasses the opportunity to initialize their own properties. Called
* after <code>{@link #afterPropertiesSet()}</code> has finished setting up
* the properties of an instance of this class.
*
* @throws FatalCacheException if one or more properties of the facade or its dependencies have
* incorrect values.
*/
protected void onAfterPropertiesSet() throws FatalCacheException {
// no implementation.
}
/**
* Cancels the update being made to the cache.
*
* @param key the key being used in the cache update.
* @throws CacheException if an unexpected error takes place when attempting to cancel the
* update.
*/
protected void onCancelCacheUpdate(Serializable key) throws CacheException {
logger.info("Cache provider does not support cancelation of updates");
}
/**
* Flushes the caches specified in the given model.
*
* @param model the model that specifies what and how to flush.
* @throws CacheException if an unexpected error takes place when flushing the cache.
*/
protected abstract void onFlushCache(FlushingModel model)
throws CacheException;
/**
* Retrieves an entry from the cache.
*
* @param key the key under which the entry is stored.
* @param model the model that specifies how to retrieve an entry.
* @return the cached entry.
* @throws CacheException if an unexpected error takes place when retrieving the entry from
* the cache.
*/
protected abstract Object onGetFromCache(Serializable key, CachingModel model)
throws CacheException;
/**
* Stores an object in the cache.
*
* @param key the key used to store the object.
* @param model the model that specifies how to store an object in the cache.
* @param obj the object to store in the cache.
* @throws CacheException if an unexpected error takes place when storing an object in the
* cache.
*/
protected abstract void onPutInCache(Serializable key, CachingModel model,
Object obj) throws CacheException;
/**
* Removes an entry from the cache.
*
* @param key the key the entry to remove is stored under.
* @param model the model that specifies how to remove the entry from the cache.
* @throws CacheException if an unexpected error takes place when removing an entry from
* the cache.
*/
protected abstract void onRemoveFromCache(Serializable key, CachingModel model)
throws CacheException;
/**
* Validates the cache manager used by this facade.
*
* @throws FatalCacheException if the cache manager is in an invalid state.
*/
protected abstract void validateCacheManager() throws FatalCacheException;
}