Commit 92c97e50 authored by Dave Cridland's avatar Dave Cridland Committed by GitHub

Merge pull request #719 from surevine/of1156

OF-1156 Warn (and possibly die) on null storage in DefaultCache
parents a8bcbc0d d913a858
...@@ -30,7 +30,7 @@ package org.jivesoftware.util.cache; ...@@ -30,7 +30,7 @@ package org.jivesoftware.util.cache;
* *
* If the cache does grow too large, objects will be removed such that those * If the cache does grow too large, objects will be removed such that those
* that are accessed least frequently are removed first. Because expiration * that are accessed least frequently are removed first. Because expiration
* happens automatically, the cache makes <b>no</b> gaurantee as to how long * happens automatically, the cache makes <b>no</b> guarantee as to how long
* an object will remain in cache after it is put in.<p> * an object will remain in cache after it is put in.<p>
* *
* Optionally, a maximum lifetime for all objects can be specified. In that * Optionally, a maximum lifetime for all objects can be specified. In that
...@@ -41,6 +41,9 @@ package org.jivesoftware.util.cache; ...@@ -41,6 +41,9 @@ package org.jivesoftware.util.cache;
* *
* All cache operations are thread safe.<p> * All cache operations are thread safe.<p>
* *
* Note that neither keys or values can be null; A {@link NullPointerException}
* will be thrown attempting to place or retrieve null values in to the cache.
*
* @see Cacheable * @see Cacheable
*/ */
public interface Cache<K,V> extends java.util.Map<K,V> { public interface Cache<K,V> extends java.util.Map<K,V> {
......
...@@ -28,6 +28,7 @@ import java.util.Map; ...@@ -28,6 +28,7 @@ import java.util.Map;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.Set; import java.util.Set;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.LinkedListNode; import org.jivesoftware.util.LinkedListNode;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -59,7 +60,11 @@ import org.slf4j.LoggerFactory; ...@@ -59,7 +60,11 @@ import org.slf4j.LoggerFactory;
*/ */
public class DefaultCache<K, V> implements Cache<K, V> { public class DefaultCache<K, V> implements Cache<K, V> {
private static final Logger Log = LoggerFactory.getLogger(DefaultCache.class); private static final String NULL_KEY_IS_NOT_ALLOWED = "Null key is not allowed!";
private static final String NULL_VALUE_IS_NOT_ALLOWED = "Null value is not allowed!";
private static final boolean allowNull = JiveGlobals.getBooleanProperty("cache.allow.null", true);
private static final Logger Log = LoggerFactory.getLogger(DefaultCache.class);
/** /**
* The map the keys and values are stored in. * The map the keys and values are stored in.
...@@ -133,6 +138,8 @@ public class DefaultCache<K, V> implements Cache<K, V> { ...@@ -133,6 +138,8 @@ public class DefaultCache<K, V> implements Cache<K, V> {
@Override @Override
public synchronized V put(K key, V value) { public synchronized V put(K key, V value) {
checkNotNull(key, NULL_KEY_IS_NOT_ALLOWED);
checkNotNull(value, NULL_VALUE_IS_NOT_ALLOWED);
// Delete an old entry if it exists. // Delete an old entry if it exists.
V answer = remove(key); V answer = remove(key);
...@@ -174,6 +181,7 @@ public class DefaultCache<K, V> implements Cache<K, V> { ...@@ -174,6 +181,7 @@ public class DefaultCache<K, V> implements Cache<K, V> {
@Override @Override
public synchronized V get(Object key) { public synchronized V get(Object key) {
checkNotNull(key, NULL_KEY_IS_NOT_ALLOWED);
// First, clear all entries that have been in cache longer than the // First, clear all entries that have been in cache longer than the
// maximum defined age. // maximum defined age.
deleteExpiredEntries(); deleteExpiredEntries();
...@@ -200,6 +208,7 @@ public class DefaultCache<K, V> implements Cache<K, V> { ...@@ -200,6 +208,7 @@ public class DefaultCache<K, V> implements Cache<K, V> {
@Override @Override
public synchronized V remove(Object key) { public synchronized V remove(Object key) {
checkNotNull(key, NULL_KEY_IS_NOT_ALLOWED);
DefaultCache.CacheObject<V> cacheObject = map.get(key); DefaultCache.CacheObject<V> cacheObject = map.get(key);
// If the object is not in cache, stop trying to remove it. // If the object is not in cache, stop trying to remove it.
if (cacheObject == null) { if (cacheObject == null) {
...@@ -285,6 +294,7 @@ public class DefaultCache<K, V> implements Cache<K, V> { ...@@ -285,6 +294,7 @@ public class DefaultCache<K, V> implements Cache<K, V> {
@Override @Override
public boolean contains(Object o) { public boolean contains(Object o) {
checkNotNull(o, NULL_KEY_IS_NOT_ALLOWED);
Iterator<V> it = iterator(); Iterator<V> it = iterator();
while (it.hasNext()) { while (it.hasNext()) {
if (it.next().equals(o)) { if (it.next().equals(o)) {
...@@ -391,6 +401,7 @@ public class DefaultCache<K, V> implements Cache<K, V> { ...@@ -391,6 +401,7 @@ public class DefaultCache<K, V> implements Cache<K, V> {
@Override @Override
public boolean containsKey(Object key) { public boolean containsKey(Object key) {
checkNotNull(key, NULL_KEY_IS_NOT_ALLOWED);
// First, clear all entries that have been in cache longer than the // First, clear all entries that have been in cache longer than the
// maximum defined age. // maximum defined age.
deleteExpiredEntries(); deleteExpiredEntries();
...@@ -409,14 +420,11 @@ public class DefaultCache<K, V> implements Cache<K, V> { ...@@ -409,14 +420,11 @@ public class DefaultCache<K, V> implements Cache<K, V> {
@Override @Override
public boolean containsValue(Object value) { public boolean containsValue(Object value) {
checkNotNull(value, NULL_VALUE_IS_NOT_ALLOWED);
// First, clear all entries that have been in cache longer than the // First, clear all entries that have been in cache longer than the
// maximum defined age. // maximum defined age.
deleteExpiredEntries(); deleteExpiredEntries();
if(value == null) {
return containsNullValue();
}
Iterator it = values().iterator(); Iterator it = values().iterator();
while(it.hasNext()) { while(it.hasNext()) {
if(value.equals(it.next())) { if(value.equals(it.next())) {
...@@ -426,16 +434,6 @@ public class DefaultCache<K, V> implements Cache<K, V> { ...@@ -426,16 +434,6 @@ public class DefaultCache<K, V> implements Cache<K, V> {
return false; return false;
} }
private boolean containsNullValue() {
Iterator it = values().iterator();
while(it.hasNext()) {
if(it.next() == null) {
return true;
}
}
return false;
}
@Override @Override
public Set<Entry<K, V>> entrySet() { public Set<Entry<K, V>> entrySet() {
// First, clear all entries that have been in cache longer than the // First, clear all entries that have been in cache longer than the
...@@ -703,4 +701,18 @@ public class DefaultCache<K, V> implements Cache<K, V> { ...@@ -703,4 +701,18 @@ public class DefaultCache<K, V> implements Cache<K, V> {
this.size = size; this.size = size;
} }
} }
private void checkNotNull(final Object argument, final String message) {
try {
if (argument == null) {
throw new NullPointerException(message);
}
} catch (NullPointerException e) {
if (allowNull) {
Log.error("Ignoring null store in Cache: ", e); // Gives us a trace for debugging.
} else {
throw e;
}
}
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment