/*
* $Id: CacheMap.java,v 1.13 2007/02/09 01:55:01 tryggvil Exp $
* Created on 6.1.2006 in project com.idega.core
*
* Copyright (C) 2006 Idega Software hf. All Rights Reserved.
*
* This software is the proprietary information of Idega hf.
* Use is subject to license terms.
*/
package com.idega.core.cache;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.Element;
import com.idega.idegaweb.IWMainApplication;
/**
* <p>
* Wrapper for the Cache implemented as a standard Map
* </p>
* Last modified: $Date: 2007/02/09 01:55:01 $ by $Author: tryggvil $
*
* @author <a href="mailto:tryggvil@idega.com">tryggvil</a>
* @version $Revision: 1.13 $
*/
public class CacheMap<K extends Serializable, V> implements Map<K, V> {
private static final Logger LOGGER = Logger.getLogger(CacheMap.class.getName());
private boolean resetable = Boolean.TRUE;
private String cacheName;
private List<CacheMapListener<K, V>> cacheListeners;
private List<CacheMapGuardian<K, V>> guardians;
CacheMap(String cacheName) {
this(cacheName, Boolean.TRUE);
}
CacheMap(String cacheName, boolean resetable) {
this(cacheName, resetable, null, null);
}
CacheMap(String cacheName, boolean resetable, CacheMapListener<K, V> cacheListener) {
this(cacheName, resetable, cacheListener, null);
}
CacheMap(String cacheName, boolean resetable, CacheMapGuardian<K, V> guardian) {
this(cacheName, resetable, null, guardian);
}
CacheMap(String cacheName, boolean resetable, CacheMapListener<K, V> cacheListener, CacheMapGuardian<K, V> guardian) {
this.cacheName = cacheName;
this.resetable = resetable;
if (cacheListener != null) {
addCacheListener(cacheListener);
}
if (guardian != null) {
addCacheGuardian(guardian);
}
}
private Cache getCache() {
return IWCacheManager2.getInstance(IWMainApplication.getDefaultIWMainApplication()).getInternalCacheManager().getCache(cacheName);
}
@Override
public int size() {
return getCache().getSize();
}
@Override
public boolean isEmpty() {
return getCache().getSize() == 0;
}
@Override
public boolean containsKey(Object key) {
try {
Element element = getCache().get((Serializable) key);
return element != null && (element.getObjectValue() != null || element.getValue() != null);
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (CacheException e) {
e.printStackTrace();
}
return false;
}
@Override
public boolean containsValue(Object value) {
throw new UnsupportedOperationException("Method containsValue not implemented");
}
@Override
@SuppressWarnings("unchecked")
public V get(Object key) {
if (key == null) {
return null;
}
try {
K realKey = (K) key;
boolean canGet = true;
if (getCacheGuardians() != null) {
for (Iterator<CacheMapGuardian<K, V>> guardiansIter = getCacheGuardians().iterator(); (guardiansIter.hasNext() && canGet);) {
canGet = guardiansIter.next().beforeGet(realKey);
}
}
if (!canGet) {
LOGGER.warning("Object can not be fetched by the key " + key + " because of the guardian(s)!");
return null;
}
Element element = getCache().get(key);
if (element == null) {
return null;
}
Object o = element.getObjectValue();
if (o == null) {
return null;
}
V result = (V) o;
if (getCacheListeners() != null) {
for (Iterator<CacheMapListener<K, V>> iterator = getCacheListeners().iterator(); iterator.hasNext();) {
CacheMapListener<K, V> listener = iterator.next();
listener.gotObject(realKey, result);
}
}
return result;
} catch (ClassCastException e) {
LOGGER.log(Level.WARNING, "Error while casting", e);
throw new RuntimeException(e);
} catch (IllegalStateException e) {
throw new RuntimeException(e);
} catch (CacheException e) {
throw new RuntimeException(e);
}
}
@Override
public V put(K key, V value) {
if (key == null) {
LOGGER.warning("Key is null, not adding value " + value + " to cache.");
return value;
}
if (value == null) {
LOGGER.warning("Value is null, not adding it to the cache by key " + key);
return value;
}
if (!(value instanceof Serializable))
LOGGER.warning("Attempting to put into the cache (name: '" + cacheName + "') not serializable object (key: '" + key +
"', value: '" + value + "'): it may cause an error while trying to serialized the cache");
try {
boolean canPut = true;
if (getCacheGuardians() != null) {
for (Iterator<CacheMapGuardian<K, V>> guardiansIter = getCacheGuardians().iterator(); (guardiansIter.hasNext() && canPut);) {
canPut = guardiansIter.next().beforePut(key, value);
}
}
if (!canPut) {
LOGGER.warning("Object " + value + " can not be put with the key " + key + " because of the guardian(s)!");
return null;
}
Cache cache = getCache();
if (cache.getCacheConfiguration().isOverflowToDisk() && !containsKey(key)) {
long maxElementsInMemory = cache.getCacheConfiguration().getMaxElementsInMemory();
long currentCacheSize = cache.getMemoryStoreSize();
if (maxElementsInMemory == currentCacheSize) {
LOGGER.info("Flushing the cache: " + cache.getName() + " because the cache size (" + currentCacheSize + ") has reached maximum: " +
maxElementsInMemory);
cache.flush();
}
}
Element element = new Element(key, value);
boolean checkTheSizes = !keySet().contains(key);
int sizeBefore = cache.getSize();
cache.put(element);
int sizeAfter = cache.getSize();
if (checkTheSizes && (sizeAfter <= 0 || sizeBefore == sizeAfter)) {
LOGGER.warning("Value '" + value + "' with key '" + key + "' was not added to the cache " + cache.getName());
return null;
}
if (getCacheListeners() != null) {
for (Iterator<CacheMapListener<K, V>> iterator = getCacheListeners().iterator(); iterator.hasNext();) {
CacheMapListener<K, V> listener = iterator.next();
listener.putObject(key, value);
}
}
return value;
} catch (IllegalStateException e) {
throw new RuntimeException(e);
}
}
@Override
@SuppressWarnings("unchecked")
public V remove(Object key) {
try {
K realKey = (K) key;
V realElementToRemove = null;
Element objectToRemove = getCache().get(realKey);
if (objectToRemove != null) {
realElementToRemove = (V) objectToRemove.getObjectValue();
}
boolean canRemove = true;
if (getCacheGuardians() != null) {
for (Iterator<CacheMapGuardian<K, V>> guardiansIter = getCacheGuardians().iterator(); (guardiansIter.hasNext() && canRemove);) {
canRemove = guardiansIter.next().beforeRemove(realKey, realElementToRemove);
}
}
if (!canRemove) {
LOGGER.warning("Object " + realElementToRemove + " can not be removed by the key " + key + " because of the guardian(s)!");
return null;
}
getCache().remove(realKey);
if (getCacheListeners() != null) {
for (Iterator<CacheMapListener<K, V>> iterator = getCacheListeners().iterator(); iterator.hasNext();) {
CacheMapListener<K, V> listener = iterator.next();
listener.removedObject(realKey);
}
}
return realElementToRemove;
} catch (ClassCastException e) {
LOGGER.log(Level.WARNING, "Error while casting", e);
throw new RuntimeException(e);
} catch (IllegalStateException e) {
throw new RuntimeException(e);
}
}
@Override
public void putAll(Map<? extends K, ? extends V> map) {
for (Iterator<? extends K> iter = map.keySet().iterator(); iter.hasNext();) {
K key = iter.next();
V value = map.get(key);
if (key != null && value != null) {
put(key, value);
}
}
}
@Override
public void clear() {
try {
if (!resetable) {
LOGGER.info("Cache " + cacheName + " is not resetable!");
return;
}
boolean canClear = true;
if (getCacheGuardians() != null) {
for (Iterator<CacheMapGuardian<K, V>> guardiansIter = getCacheGuardians().iterator(); (guardiansIter.hasNext() && canClear);) {
canClear = guardiansIter.next().beforeClear();
}
}
if (!canClear) {
LOGGER.warning("Cache " + cacheName + " can not be cleared because of the guardian(s)!");
return;
}
if (getCache().getSize() > 0) {
getCache().removeAll();
LOGGER.info("Cleared cache: " + cacheName);
}
if (getCacheListeners() != null) {
for (Iterator<CacheMapListener<K, V>> iterator = getCacheListeners().iterator(); iterator.hasNext();) {
CacheMapListener<K, V> listener = iterator.next();
listener.cleared();
}
}
} catch (IllegalStateException e) {
throw new RuntimeException(e);
}
}
@Override
@SuppressWarnings("unchecked")
public Set<K> keySet() {
Set<K> set = new HashSet<K>();
List<K> keys;
try {
keys = getCache().getKeys();
set.addAll(keys);
return set;
} catch (IllegalStateException e) {
throw new RuntimeException(e);
} catch (CacheException e) {
throw new RuntimeException(e);
}
}
@Override
public Collection<V> values() {
Collection<V> values = new ArrayList<V>();
for (Iterator<K> iter = keySet().iterator(); iter.hasNext();) {
K key = iter.next();
V value = get(key);
if (value != null) {
values.add(value);
}
}
return values;
}
@Override
public Set<Map.Entry<K, V>> entrySet() {
throw new UnsupportedOperationException("Method entrySet() not implemented");
}
/**
* @return Returns the cacheListeners.
*/
public List<CacheMapListener<K, V>> getCacheListeners() {
return this.cacheListeners;
}
/**
* @param cacheListeners The cacheListeners to set.
*/
public void setCacheListeners(List<CacheMapListener<K, V>> cacheListeners) {
this.cacheListeners = cacheListeners;
}
/**
* @return Returns the cacheListeners.
*/
public void addCacheListener(CacheMapListener<K, V> listener) {
List<CacheMapListener<K, V>> cacheListeners = getCacheListeners();
if (cacheListeners == null) {
cacheListeners = new ArrayList<CacheMapListener<K, V>>();
setCacheListeners(cacheListeners);
}
cacheListeners.add(listener);
}
public List<CacheMapGuardian<K, V>> getCacheGuardians() {
return this.guardians;
}
public void setCacheGuardians(List<CacheMapGuardian<K, V>> guardians) {
this.guardians = guardians;
}
public void addCacheGuardian(CacheMapGuardian<K, V> guardian) {
if (getCacheGuardians() == null) {
guardians = new ArrayList<CacheMapGuardian<K, V>>();
setCacheGuardians(guardians);
}
guardians.add(guardian);
}
@Override
public String toString() {
return "Cache: " + cacheName + ", size: " + size() + ", keys: " + keySet() + ", values: " + values();
}
}