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/22 22:34:26 UTC
cvs commit: jakarta-turbine-jcs/sandbox/yajcache/src/org/apache/jcs/yajcache/soft KeyedSoftRef.java KeyedSoftRefCollector.java SoftRefCache.java SoftRefCacheCleaner.java SoftRefCacheSafe.java
hchar 2005/01/22 13:34:26
Added: sandbox/yajcache/src/org/apache/jcs/yajcache/soft
KeyedSoftRef.java KeyedSoftRefCollector.java
SoftRefCache.java SoftRefCacheCleaner.java
SoftRefCacheSafe.java
Log:
move yajcache to sandbox
Revision Changes Path
1.1 jakarta-turbine-jcs/sandbox/yajcache/src/org/apache/jcs/yajcache/soft/KeyedSoftRef.java
Index: KeyedSoftRef.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.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import org.apache.jcs.yajcache.annotate.*;
/**
* Soft reference with an embedded key.
*
* @author Hanson Char
*/
@CopyRightApache
class KeyedSoftRef<T> extends SoftReference<T> {
private final String key;
// KeyedSoftRef(String key, T value) {
// super(value);
// this.key = key;
// }
KeyedSoftRef(String key, T value, ReferenceQueue<? super T> q) {
super(value, q);
this.key = key;
}
public String getKey() {
return this.key;
}
}
1.1 jakarta-turbine-jcs/sandbox/yajcache/src/org/apache/jcs/yajcache/soft/KeyedSoftRefCollector.java
Index: KeyedSoftRefCollector.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.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.jcs.yajcache.annotate.*;
/**
* Collects and clears stale cache entries implemented using Soft References.
*
* @author Hanson Char
*/
@CopyRightApache
class KeyedSoftRefCollector<V> implements Runnable {
private static final boolean debug = true;
private Log log = debug ? LogFactory.getLog(this.getClass()) : null;
private volatile int count;
private final ReferenceQueue<V> q;
private final Map<String, KeyedSoftRef<V>> map;
/** Creates a new instance of ReferenceQProcessor */
KeyedSoftRefCollector(ReferenceQueue<V> q, Map<String, KeyedSoftRef<V>> map) {
this.q = q;
this.map = map;
}
/**
* Removes stale entries from the cache map collected by GC.
* Thread safetyness provided by ReferenceQueue.
*/
// @SuppressWarnings("unchecked")
public void run() {
Reference<? extends V> r;
while ((r = this.q.poll()) != null) {
KeyedSoftRef ksr = (KeyedSoftRef)r;
String key = ksr.getKey();
if (debug)
log.debug("Remove stale entry with key=" + key);
SoftRefCacheCleaner.inst.cleanupKey(map, key);
// referent should have been cleared. Defensively clear it again.
ksr.clear();
count++;
}
}
public int getCount() {
return count;
}
}
1.1 jakarta-turbine-jcs/sandbox/yajcache/src/org/apache/jcs/yajcache/soft/SoftRefCache.java
Index: SoftRefCache.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.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 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.annotate.*;
/**
* Cache implemented using Soft References.
*
* @author Hanson Char
*/
@CopyRightApache
@TODO("Annotate the thread-safetyness of the methods")
public class SoftRefCache<V> implements ICache<V> {
private static final boolean debug = true;
private Log log = debug ? LogFactory.getLog(this.getClass()) : null;
private final ReferenceQueue<V> refq = new ReferenceQueue<V>();
private final String name;
private final Class<V> valueType;
private final Map<String, KeyedSoftRef<V>> map;
private final KeyedSoftRefCollector<V> collector;
public String getName() {
return this.name;
}
public Class<V> getValueType() {
return this.valueType;
}
public SoftRefCache(String name, Class<V> valueType, int initialCapacity,
float loadFactor, int concurrencyLevel)
{
map = new ConcurrentHashMap<String,KeyedSoftRef<V>>(initialCapacity, loadFactor, concurrencyLevel);
collector = new KeyedSoftRefCollector<V>(refq, map);
this.name = name;
this.valueType = valueType;
}
public SoftRefCache(String name, Class<V> valueType, int initialCapacity) {
map = new ConcurrentHashMap<String,KeyedSoftRef<V>>(initialCapacity);
collector = new KeyedSoftRefCollector<V>(refq, map);
this.name = name;
this.valueType = valueType;
}
public SoftRefCache(String name, Class<V> valueType) {
map = new ConcurrentHashMap<String,KeyedSoftRef<V>>();
collector = new KeyedSoftRefCollector<V>(refq, map);
this.name = name;
this.valueType = valueType;
}
public boolean isEmpty() {
this.collector.run();
return map.isEmpty();
}
public int size() {
this.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(String key) {
this.collector.run();
KeyedSoftRef<V> ref = map.get(key);
if (ref == null)
return null;
V val = ref.get();
if (val == null) {
// already garbage collected. So try to clean up the key.
SoftRefCacheCleaner.inst.cleanupKey(this.map, key);
}
// cache value exists.
// try to refresh the soft reference.
// this.renewSoftReference(key, val);
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(Object key) {
return key == null ? null : this.get(key.toString());
}
public V put(String key, V value) {
this.collector.run();
KeyedSoftRef<V> oldRef = map.put(key, new KeyedSoftRef<V>(key, value, refq));
if (oldRef == null)
return null;
V ret = oldRef.get();
oldRef.clear();
if (!value.equals(ret)) {
// value changed for the key
this.publishFlushKey(key);
}
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(String key) {
}
public void putAll(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(String key) {
this.collector.run();
KeyedSoftRef<V> oldRef = map.remove(key);
if (oldRef == null)
return null;
this.publishFlushKey(key);
V ret = oldRef.get();
oldRef.clear();
return ret;
}
public V remove(Object key) {
return key == null ? null : this.remove(key.toString());
}
public void clear() {
this.collector.run();
map.clear();
}
public Set<String> keySet() {
this.collector.run();
return map.keySet();
}
public Set<Map.Entry<String,V>> entrySet() {
this.collector.run();
Set<Map.Entry<String,KeyedSoftRef<V>>> fromSet = map.entrySet();
Set<Map.Entry<String,V>> toSet = new HashSet<Map.Entry<String,V>>();
for (final Map.Entry<String,KeyedSoftRef<V>> item : fromSet) {
KeyedSoftRef<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 Collection<V> values() {
this.collector.run();
Collection<KeyedSoftRef<V>> fromSet = map.values();
List<V> toCol = new ArrayList<V>(fromSet.size());
for (final KeyedSoftRef<V> ref : fromSet) {
V val = ref.get();
if (val != null) {
toCol.add(val);
}
}
return toCol;
}
public boolean containsKey(Object key) {
if (key == null)
return false;
return this.get(key.toString()) != null;
}
public boolean containsValue(Object value) {
this.collector.run();
Collection<KeyedSoftRef<V>> fromSet = map.values();
for (final KeyedSoftRef<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();
}
}
1.1 jakarta-turbine-jcs/sandbox/yajcache/src/org/apache/jcs/yajcache/soft/SoftRefCacheCleaner.java
Index: SoftRefCacheCleaner.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.util.Map;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.jcs.yajcache.annotate.*;
/**
*
* @author Hanson Char
*/
@CopyRightApache
enum SoftRefCacheCleaner {
inst;
private static final boolean debug = true;
private Log log = debug ? LogFactory.getLog(this.getClass()) : null;
private volatile int countTryKeyClean;
private volatile int countRemovedByOthers;
private volatile int countKeyCleaned;
private volatile int countDataRace;
private volatile int countDataRaceAndRemovedByOthers;
private volatile int countBye;
<V> void cleanupKey(Map<String, KeyedSoftRef<V>> map, String key) {
V val = null;
// already garbage collected. So try to clean up the key.
if (debug)
log.debug("Try to clean up the key");
this.countTryKeyClean++;
KeyedSoftRef<V> oldRef = map.remove(key);
// If oldRef is null, the key has just been
// cleaned up by another thread.
if (oldRef == null) {
if (debug)
log.debug("Key has just been removed by another thread.");
this.countRemovedByOthers++;
return;
}
// Check for race condition.
V oldVal = oldRef.get();
if (val == oldVal) {
// not considered a race condition
oldRef.clear();
if (debug)
log.debug("Key removed and Soft Reference cleared.");
this.countKeyCleaned++;
return;
}
// Race condition.
do {
if (debug)
log.debug("Race condition occurred. So put back the old stuff.");
this.countDataRace++;
// race condition occurred
// put back the old stuff
val = oldVal;
oldRef = map.put(key, oldRef);
if (oldRef == null) {
// key has just been cleaned up by another thread.
if (debug)
log.debug("Key has just been removed by another thread.");
this.countDataRaceAndRemovedByOthers++;
return;
}
oldVal = oldRef.get();
} while (oldVal != val);
if (debug)
log.debug("Bye.");
this.countBye++;
return;
}
public int getCountTryKeyClean() {
return countTryKeyClean;
}
public int getCountRemovedByOthers() {
return countRemovedByOthers;
}
public int getCountKeyCleaned() {
return countKeyCleaned;
}
public int getCountDataRace() {
return countDataRace;
}
public int getCountDataRaceAndRemovedByOthers() {
return countDataRaceAndRemovedByOthers;
}
public int getCountBye() {
return countBye;
}
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
1.1 jakarta-turbine-jcs/sandbox/yajcache/src/org/apache/jcs/yajcache/soft/SoftRefCacheSafe.java
Index: SoftRefCacheSafe.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.annotate.*;
/**
*
* @author Hanson Char
*/
@CopyRightApache
@TODO("Annotate the thread-safetyness of the methods")
public class SoftRefCacheSafe<V> extends SoftRefCache<V>
implements ICacheSafe<V>
{
public SoftRefCacheSafe(String name, Class<V> valueType,
int initialCapacity, float loadFactor, int concurrencyLevel)
{
super(name, valueType, initialCapacity, loadFactor, concurrencyLevel);
}
public SoftRefCacheSafe(String name, Class<V> valueType,
int initialCapacity)
{
super(name, valueType, initialCapacity);
}
public SoftRefCacheSafe(String name, Class<V> valueType) {
super(name, valueType);
}
public V getCopy(String key) {
V val = this.get(key);
return this.dup(val);
}
public V putCopy(String key, V value) {
return this.put(key, this.dup(value));
}
public void putAll(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(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(String key) {
V val = this.get(key);
return BeanUtils.inst.cloneDeep(val);
}
public V putBeanCopy(String key, V value) {
return this.put(key, BeanUtils.inst.cloneDeep(value));
}
public void putAllBeanCopies(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(String key) {
V val = this.get(key);
return BeanUtils.inst.cloneShallow(val);
}
public V putBeanClone(String key, V value) {
return this.put(key, BeanUtils.inst.cloneShallow(value));
}
public void putAllBeanClones(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