You are viewing a plain text version of this content. The canonical link for it is here.
Posted to jcs-dev@jakarta.apache.org by hc...@apache.org on 2005/01/27 12:02:12 UTC
cvs commit: jakarta-turbine-jcs/sandbox/yajcache/src/org/apache/jcs/yajcache/soft SoftRefFileCache.java SoftRefFileCacheSafe.java
hchar 2005/01/27 03:02:12
Added: sandbox/yajcache/src/org/apache/jcs/yajcache/soft
SoftRefFileCache.java SoftRefFileCacheSafe.java
Log:
soft reference memory cache backed up by file
Revision Changes Path
1.1 jakarta-turbine-jcs/sandbox/yajcache/src/org/apache/jcs/yajcache/soft/SoftRefFileCache.java
Index: SoftRefFileCache.java
===================================================================
/*
* Copyright 2001-2004 The Apache Software Foundation.
*
* 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.apache.jcs.yajcache.soft;
import java.io.Serializable;
import java.lang.ref.ReferenceQueue;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Lock;
import org.apache.commons.lang.SerializationUtils;
import org.apache.jcs.yajcache.core.CacheEntry;
import org.apache.jcs.yajcache.core.ICache;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.jcs.yajcache.lang.annotation.*;
import org.apache.jcs.yajcache.config.PerCacheConfig;
import org.apache.jcs.yajcache.core.ICacheChangeListener;
import org.apache.jcs.yajcache.core.CacheChangeSupport;
import org.apache.jcs.yajcache.file.CacheFileContent;
import org.apache.jcs.yajcache.file.CacheFileContentType;
import org.apache.jcs.yajcache.file.CacheFileDAO;
import org.apache.jcs.yajcache.lang.ref.KeyedRefCollector;
import org.apache.jcs.yajcache.lang.ref.KeyedSoftReference;
import org.apache.jcs.yajcache.util.concurrent.locks.IKeyedReadWriteLock;
import org.apache.jcs.yajcache.util.concurrent.locks.KeyedReadWriteLock;
/**
* Cache implemented using Soft References.
*
* @author Hanson Char
*/
@CopyRightApache
@TODO("Annotate the thread-safetyness of the methods")
public class SoftRefFileCache<V> implements ICache<V>
{
private static final boolean debug = true;
private Log log = debug ? LogFactory.getLog(this.getClass()) : null;
private final @NonNullable ReferenceQueue<V> refq = new ReferenceQueue<V>();
private final @NonNullable String name;
private final @NonNullable Class<V> valueType;
private final @NonNullable ConcurrentMap<String, KeyedSoftReference<V>> map;
private final @NonNullable PerCacheConfig config;
private final @NonNullable KeyedRefCollector collector;
private final IKeyedReadWriteLock krwl = new KeyedReadWriteLock();
// private final @NonNullable ConcurrentMap<String, CacheOp[]> synMap =
// new ConcurrentHashMap<String, CacheOp[]>();
private final @NonNullable CacheChangeSupport<V> cacheChangeSupport =
new CacheChangeSupport<V>(this);
private volatile int countGet;
private volatile int countGetHitMemory;
private volatile int countGetHitFile;
private volatile int countGetMiss;
private volatile int countPut;
private volatile int countRemove;
public String getName() {
return this.name;
}
public Class<V> getValueType() {
return this.valueType;
}
public SoftRefFileCache(
@NonNullable String name, @NonNullable Class<V> valueType,
@NonNullable PerCacheConfig config,
int initialCapacity,float loadFactor, int concurrencyLevel)
{
map = new ConcurrentHashMap<String,KeyedSoftReference<V>>(
initialCapacity, loadFactor, concurrencyLevel);
collector = new KeyedRefCollector(refq, map);
this.name = name;
this.valueType = valueType;
this.config = config;
}
public SoftRefFileCache(
@NonNullable String name, @NonNullable Class<V> valueType,
@NonNullable PerCacheConfig config,
int initialCapacity)
{
map = new ConcurrentHashMap<String,KeyedSoftReference<V>>(initialCapacity);
collector = new KeyedRefCollector(refq, map);
this.name = name;
this.valueType = valueType;
this.config = config;
}
public SoftRefFileCache(@NonNullable String name,
@NonNullable Class<V> valueType,
PerCacheConfig config)
{
map = new ConcurrentHashMap<String,KeyedSoftReference<V>>();
collector = new KeyedRefCollector(refq, map);
this.name = name;
this.valueType = valueType;
this.config = config;
}
public boolean isEmpty() {
collector.run();
return map.isEmpty();
}
public int size() {
collector.run();
return map.size();
}
// @tothink: SoftReference.get() doesn't seem to be thread-safe.
// But do we really want to synchronize upon invoking get() ?
// It's not thread-safe, but what's the worst consequence ?
public V get(@NonNullable String key) {
collector.run();
Lock lock = this.krwl.readLock(key);
lock.lock();
try {
return doGet(key);
} finally {
lock.unlock();
}
}
private V doGet(String key) {
KeyedSoftReference<V> ref = map.get(key);
V val = null;
if (ref != null)
val = ref.get();
if (val == null) {
// Not in memory.
if (ref != null) {
// GC'd. So try to clean up the key/ref pair.
this.map.remove(key, ref);
}
// Get from the file system.
CacheFileContent cfc = CacheFileDAO.inst.readCacheItem(this.name, key);
if (cfc == null) {
// Not in file system.
return null;
}
// Found in file system.
val = (V)cfc.deserialize();
if (val == null) {
// Corrupted file. Try remove it from file system.
CacheFileDAO.inst.removeCacheItem(this.name, key);
return null;
}
// Resurrect item back to memory.
map.putIfAbsent(key,
new KeyedSoftReference<V>(key, val, refq));
}
// cache value exists.
return val;
}
// private void renewSoftReference(String key, V val) {
// if (debug)
// log.debug("get: try to refresh the soft reference.");
// KeyedSoftRef<V> oldRef =
// map.put(key, new KeyedSoftRef<V>(key, val, refq));
// // Check for race conditon.
// if (oldRef == null) {
// // key has just been removed by another thread.
// if (debug)
// log.debug("get: key has just been removed by another thread.");
// return;
// }
// V oldVal = oldRef.get();
// // if oldVal is null, it means the GC just cleared it.
// while (oldVal != null && oldVal != val) {
// // race condition occurred
// // put back the old stuff
// if (debug)
// log.debug("get: race condition occurred. put back the old stuff");
// val = oldVal;
// oldRef = map.put(key, oldRef);
//
// if (oldRef == null) {
// // key has just been removed by another thread.
// if (debug)
// log.debug("get: key has just been removed by another thread.");
// oldRef = map.remove(key);
//
// if (oldRef == null) {
// // again, key has just been removed by another thread.
// if (debug)
// log.debug("again: key has just been removed by another thread.");
// break;
// }
// }
// oldVal = oldRef.get();
// }
// return;
// }
public V get(@NonNullable Object key) {
return this.get(key.toString());
}
public V put(@NonNullable String key, @NonNullable V value) {
this.collector.run();
Lock lock = this.krwl.writeLock(key);
lock.lock();
try {
return doPut(key, value);
} finally {
lock.unlock();
}
}
private V doPut(@NonNullable String key, @NonNullable V value) {
KeyedSoftReference<V> oldRef =
map.put(key, new KeyedSoftReference<V>(key, value, refq));
V ret = null;
if (oldRef != null) {
ret = oldRef.get();
oldRef.clear();
}
if (ret == null) {
// Not in memory.
if (value instanceof Serializable) {
// Try the file system.
CacheFileContent cfc =
CacheFileDAO.inst.readCacheItem(this.name, key);
if (cfc != null)
ret = (V)cfc.deserialize();
if (!value.equals(ret)) {
// Considered new value being put to memory.
// So persist to file system.
byte[] ba = SerializationUtils.serialize((Serializable)value);
CacheFileDAO.inst.writeCacheItem(
this.name, CacheFileContentType.JAVA_SERIALIZATION, key, ba);
}
}
return ret;
}
// ret must be non-null.
// Found in memory
if (!value.equals(ret)) {
// Different value being put to memory.
if (value instanceof Serializable) {
// Persist to file system.
byte[] ba = SerializationUtils.serialize((Serializable)value);
CacheFileDAO.inst.writeCacheItem(
this.name, CacheFileContentType.JAVA_SERIALIZATION, key, ba);
}
}
return ret;
}
@TODO(
value="Queue up a flush event for the key.",
details="This is useful for synchronizing caches in a cluster environment."
)
private void publishFlushKey(@NonNullable String key) {
}
public void putAll(@NonNullable Map<? extends String, ? extends V> map) {
for (final Map.Entry<? extends String, ? extends V> e : map.entrySet())
this.put(e.getKey(), e.getValue());
}
public V remove(@NonNullable String key) {
this.collector.run();
Lock lock = this.krwl.writeLock(key);
lock.lock();
try {
return doRemove(key);
} finally {
lock.unlock();
}
}
private V doRemove(@NonNullable String key) {
KeyedSoftReference<V> oldRef = map.remove(key);
V ret = null;
if (oldRef != null) {
// may exist in memory
ret = oldRef.get();
oldRef.clear();
}
if (ret == null) {
// not exist or no longer exist in memory;
// so check the file system.
CacheFileContent cfc =
CacheFileDAO.inst.readCacheItem(this.name, key);
if (cfc == null) {
// not exist in file system.
return null;
}
if (cfc != null) {
// If corrupted, invoking deserialize will return null.
ret = (V)cfc.deserialize();
}
}
// Must exist the file system, corrupted or not.
CacheFileDAO.inst.removeCacheItem(this.name, key);
return ret;
}
public V remove(@NonNullable Object key) {
return key == null ? null : this.remove(key.toString());
}
public void clear() {
// this.collector.run();
map.clear();
this.cacheChangeSupport.fireCacheClear();
}
public @NonNullable Set<String> keySet() {
// this.collector.run();
return map.keySet();
}
public @NonNullable Set<Map.Entry<String,V>> entrySet() {
// this.collector.run();
Set<Map.Entry<String,KeyedSoftReference<V>>> fromSet = map.entrySet();
Set<Map.Entry<String,V>> toSet = new HashSet<Map.Entry<String,V>>();
for (final Map.Entry<String, KeyedSoftReference<V>> item : fromSet) {
KeyedSoftReference<V> ref = item.getValue();
V val = ref.get();
if (val != null) {
Map.Entry<String,V> e = new CacheEntry<V>(item.getKey(), val);
toSet.add(e);
}
}
return toSet;
}
public @NonNullable Collection<V> values() {
// this.collector.run();
Collection<KeyedSoftReference<V>> fromSet = map.values();
List<V> toCol = new ArrayList<V>(fromSet.size());
for (final KeyedSoftReference<V> ref : fromSet) {
V val = ref.get();
if (val != null) {
toCol.add(val);
}
}
return toCol;
}
public boolean containsKey(@NonNullable Object key) {
return this.get(key.toString()) != null;
}
public boolean containsValue(@NonNullable Object value) {
// this.collector.run();
Collection<KeyedSoftReference<V>> fromSet = map.values();
for (final KeyedSoftReference<V> ref : fromSet) {
V val = ref.get();
if (value.equals(val))
return true;
}
return false;
}
/** Returns the number of Soft References collected by GC. */
// public int getCollectorCount() {
// return this.collector.getCount();
// }
public void addCacheChangeListener(@NonNullable ICacheChangeListener<V> listener)
{
this.cacheChangeSupport.addCacheChangeListener(listener);
}
public void removeCacheChangeListener(@NonNullable ICacheChangeListener<V> listener)
{
this.cacheChangeSupport.removeCacheChangeListener(listener);
}
}
1.1 jakarta-turbine-jcs/sandbox/yajcache/src/org/apache/jcs/yajcache/soft/SoftRefFileCacheSafe.java
Index: SoftRefFileCacheSafe.java
===================================================================
/*
* Copyright 2001-2004 The Apache Software Foundation.
*
* 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.apache.jcs.yajcache.soft;
import java.io.Serializable;
import java.util.Map;
import org.apache.jcs.yajcache.core.ICacheSafe;
import org.apache.jcs.yajcache.util.BeanUtils;
import org.apache.jcs.yajcache.util.SerializeUtils;
import org.apache.jcs.yajcache.lang.annotation.*;
import org.apache.jcs.yajcache.config.PerCacheConfig;
/**
*
* @author Hanson Char
*/
@CopyRightApache
@TODO("Annotate the thread-safetyness of the methods")
public class SoftRefFileCacheSafe<V> extends SoftRefFileCache<V>
implements ICacheSafe<V>
{
public SoftRefFileCacheSafe(@NonNullable String name, @NonNullable Class<V> valueType,
PerCacheConfig config,
int initialCapacity, float loadFactor, int concurrencyLevel)
{
super(name, valueType, config, initialCapacity, loadFactor, concurrencyLevel);
}
public SoftRefFileCacheSafe(@NonNullable String name, @NonNullable Class<V> valueType,
PerCacheConfig config,
int initialCapacity)
{
super(name, valueType, config, initialCapacity);
}
public SoftRefFileCacheSafe(@NonNullable String name,
@NonNullable Class<V> valueType, PerCacheConfig config)
{
super(name, valueType, config);
}
public V getCopy(@NonNullable String key) {
V val = this.get(key);
return this.dup(val);
}
public V putCopy(@NonNullable String key, @NonNullable V value) {
return this.put(key, this.dup(value));
}
public void putAll(@NonNullable Map<? extends String, ? extends V> map) {
for (final Map.Entry<? extends String, ? extends V> e : map.entrySet())
this.put(e.getKey(), e.getValue());
}
public void putAllCopies(@NonNullable Map<? extends String, ? extends V> map) {
for (final Map.Entry<? extends String, ? extends V> e : map.entrySet())
this.put(e.getKey(), this.dup(e.getValue()));
}
public V getBeanCopy(@NonNullable String key) {
V val = this.get(key);
return BeanUtils.inst.cloneDeep(val);
}
public V putBeanCopy(@NonNullable String key, @NonNullable V value) {
return this.put(key, BeanUtils.inst.cloneDeep(value));
}
public void putAllBeanCopies(@NonNullable Map<? extends String, ? extends V> map) {
for (final Map.Entry<? extends String, ? extends V> e : map.entrySet())
this.put(e.getKey(), BeanUtils.inst.cloneDeep(e.getValue()));
}
public V getBeanClone(@NonNullable String key) {
V val = this.get(key);
return BeanUtils.inst.cloneShallow(val);
}
public V putBeanClone(@NonNullable String key, @NonNullable V value) {
return this.put(key, BeanUtils.inst.cloneShallow(value));
}
public void putAllBeanClones(@NonNullable Map<? extends String, ? extends V> map) {
for (final Map.Entry<? extends String, ? extends V> e : map.entrySet())
this.put(e.getKey(), BeanUtils.inst.cloneShallow(e.getValue()));
}
private V dup(V val) {
if (val instanceof Serializable) {
return (V)SerializeUtils.inst.dup((Serializable)val);
}
return val;
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: turbine-jcs-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: turbine-jcs-dev-help@jakarta.apache.org