diff --git a/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheCache.java b/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheCache.java index 31bfd2f530c..486634146d0 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheCache.java +++ b/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheCache.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -63,7 +63,7 @@ public class EhCacheCache implements Cache { @Override public ValueWrapper get(Object key) { Element element = this.cache.get(key); - return toWrapper(element); + return toValueWrapper(element); } @Override @@ -85,7 +85,7 @@ public class EhCacheCache implements Cache { @Override public ValueWrapper putIfAbsent(Object key, Object value) { Element existingElement = this.cache.putIfAbsent(new Element(key, value)); - return toWrapper(existingElement); + return toValueWrapper(existingElement); } @Override @@ -98,7 +98,7 @@ public class EhCacheCache implements Cache { this.cache.removeAll(); } - private ValueWrapper toWrapper(Element element) { + private ValueWrapper toValueWrapper(Element element) { return (element != null ? new SimpleValueWrapper(element.getObjectValue()) : null); } diff --git a/spring-context-support/src/main/java/org/springframework/cache/guava/GuavaCache.java b/spring-context-support/src/main/java/org/springframework/cache/guava/GuavaCache.java index 7e8b71429e8..55e3c9a3875 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/guava/GuavaCache.java +++ b/spring-context-support/src/main/java/org/springframework/cache/guava/GuavaCache.java @@ -16,20 +16,18 @@ package org.springframework.cache.guava; -import java.io.Serializable; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import com.google.common.cache.LoadingCache; import com.google.common.util.concurrent.UncheckedExecutionException; -import org.springframework.cache.Cache; -import org.springframework.cache.support.SimpleValueWrapper; +import org.springframework.cache.support.AbstractValueAdaptingCache; import org.springframework.util.Assert; /** - * Spring {@link Cache} adapter implementation on top of a - * Guava {@link com.google.common.cache.Cache} instance. + * Spring {@link org.springframework.cache.Cache} adapter implementation + * on top of a Guava {@link com.google.common.cache.Cache} instance. * *

Requires Google Guava 12.0 or higher. * @@ -37,16 +35,12 @@ import org.springframework.util.Assert; * @author Stephane Nicoll * @since 4.0 */ -public class GuavaCache implements Cache { - - private static final Object NULL_HOLDER = new NullHolder(); +public class GuavaCache extends AbstractValueAdaptingCache { private final String name; private final com.google.common.cache.Cache cache; - private final boolean allowNullValues; - /** * Create a {@link GuavaCache} instance with the specified name and the @@ -67,11 +61,11 @@ public class GuavaCache implements Cache { * values for this cache */ public GuavaCache(String name, com.google.common.cache.Cache cache, boolean allowNullValues) { + super(allowNullValues); Assert.notNull(name, "Name must not be null"); Assert.notNull(cache, "Cache must not be null"); this.name = name; this.cache = cache; - this.allowNullValues = allowNullValues; } @@ -85,32 +79,23 @@ public class GuavaCache implements Cache { return this.cache; } - public final boolean isAllowNullValues() { - return this.allowNullValues; - } - @Override public ValueWrapper get(Object key) { if (this.cache instanceof LoadingCache) { try { Object value = ((LoadingCache) this.cache).get(key); - return toWrapper(value); + return toValueWrapper(value); } catch (ExecutionException ex) { throw new UncheckedExecutionException(ex.getMessage(), ex); } } - return toWrapper(this.cache.getIfPresent(key)); + return super.get(key); } @Override - @SuppressWarnings("unchecked") - public T get(Object key, Class type) { - Object value = fromStoreValue(this.cache.getIfPresent(key)); - if (value != null && type != null && !type.isInstance(value)) { - throw new IllegalStateException("Cached value is not of required type [" + type.getName() + "]: " + value); - } - return (T) value; + protected Object lookup(Object key) { + return this.cache.getIfPresent(key); } @Override @@ -123,7 +108,7 @@ public class GuavaCache implements Cache { try { PutIfAbsentCallable callable = new PutIfAbsentCallable(value); Object result = this.cache.get(key, callable); - return (callable.called ? null : toWrapper(result)); + return (callable.called ? null : toValueWrapper(result)); } catch (ExecutionException ex) { throw new IllegalStateException(ex); @@ -141,42 +126,6 @@ public class GuavaCache implements Cache { } - /** - * Convert the given value from the internal store to a user value - * returned from the get method (adapting {@code null}). - * @param storeValue the store value - * @return the value to return to the user - */ - protected Object fromStoreValue(Object storeValue) { - if (this.allowNullValues && storeValue == NULL_HOLDER) { - return null; - } - return storeValue; - } - - /** - * Convert the given user value, as passed into the put method, - * to a value in the internal store (adapting {@code null}). - * @param userValue the given user value - * @return the value to store - */ - protected Object toStoreValue(Object userValue) { - if (this.allowNullValues && userValue == null) { - return NULL_HOLDER; - } - return userValue; - } - - private ValueWrapper toWrapper(Object value) { - return (value != null ? new SimpleValueWrapper(fromStoreValue(value)) : null); - } - - - @SuppressWarnings("serial") - private static class NullHolder implements Serializable { - } - - private class PutIfAbsentCallable implements Callable { private final Object value; diff --git a/spring-context-support/src/main/java/org/springframework/cache/jcache/JCacheCache.java b/spring-context-support/src/main/java/org/springframework/cache/jcache/JCacheCache.java index 372e754c9fa..d149a60ba35 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/jcache/JCacheCache.java +++ b/spring-context-support/src/main/java/org/springframework/cache/jcache/JCacheCache.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,10 +16,7 @@ package org.springframework.cache.jcache; -import java.io.Serializable; - -import org.springframework.cache.Cache; -import org.springframework.cache.support.SimpleValueWrapper; +import org.springframework.cache.support.AbstractValueAdaptingCache; import org.springframework.util.Assert; /** @@ -32,14 +29,10 @@ import org.springframework.util.Assert; * @author Stephane Nicoll * @since 3.2 */ -public class JCacheCache implements Cache { - - private static final Object NULL_HOLDER = new NullHolder(); +public class JCacheCache extends AbstractValueAdaptingCache { private final javax.cache.Cache cache; - private final boolean allowNullValues; - /** * Create an {@link org.springframework.cache.jcache.JCacheCache} instance. @@ -55,9 +48,9 @@ public class JCacheCache implements Cache { * @param allowNullValues whether to accept and convert null values for this cache */ public JCacheCache(javax.cache.Cache jcache, boolean allowNullValues) { + super(allowNullValues); Assert.notNull(jcache, "Cache must not be null"); this.cache = jcache; - this.allowNullValues = allowNullValues; } @@ -71,24 +64,9 @@ public class JCacheCache implements Cache { return this.cache; } - public final boolean isAllowNullValues() { - return this.allowNullValues; - } - @Override - public ValueWrapper get(Object key) { - Object value = this.cache.get(key); - return (value != null ? new SimpleValueWrapper(fromStoreValue(value)) : null); - } - - @Override - @SuppressWarnings("unchecked") - public T get(Object key, Class type) { - Object value = fromStoreValue(this.cache.get(key)); - if (value != null && type != null && !type.isInstance(value)) { - throw new IllegalStateException("Cached value is not of required type [" + type.getName() + "]: " + value); - } - return (T) value; + protected Object lookup(Object key) { + return this.cache.get(key); } @Override @@ -112,36 +90,4 @@ public class JCacheCache implements Cache { this.cache.removeAll(); } - - /** - * Convert the given value from the internal store to a user value - * returned from the get method (adapting {@code null}). - * @param storeValue the store value - * @return the value to return to the user - */ - protected Object fromStoreValue(Object storeValue) { - if (this.allowNullValues && storeValue == NULL_HOLDER) { - return null; - } - return storeValue; - } - - /** - * Convert the given user value, as passed into the put method, - * to a value in the internal store (adapting {@code null}). - * @param userValue the given user value - * @return the value to store - */ - protected Object toStoreValue(Object userValue) { - if (this.allowNullValues && userValue == null) { - return NULL_HOLDER; - } - return userValue; - } - - - @SuppressWarnings("serial") - private static class NullHolder implements Serializable { - } - } diff --git a/spring-context/src/main/java/org/springframework/cache/concurrent/ConcurrentMapCache.java b/spring-context/src/main/java/org/springframework/cache/concurrent/ConcurrentMapCache.java index bb3a472e15f..4942e14c0f7 100644 --- a/spring-context/src/main/java/org/springframework/cache/concurrent/ConcurrentMapCache.java +++ b/spring-context/src/main/java/org/springframework/cache/concurrent/ConcurrentMapCache.java @@ -16,17 +16,15 @@ package org.springframework.cache.concurrent; -import java.io.Serializable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import org.springframework.cache.Cache; -import org.springframework.cache.support.SimpleValueWrapper; +import org.springframework.cache.support.AbstractValueAdaptingCache; import org.springframework.util.Assert; /** - * Simple {@link Cache} implementation based on the core JDK - * {@code java.util.concurrent} package. + * Simple {@link org.springframework.cache.Cache} implementation based on the + * core JDK {@code java.util.concurrent} package. * *

Useful for testing or simple caching scenarios, typically in combination * with {@link org.springframework.cache.support.SimpleCacheManager} or @@ -41,16 +39,12 @@ import org.springframework.util.Assert; * @author Juergen Hoeller * @since 3.1 */ -public class ConcurrentMapCache implements Cache { - - private static final Object NULL_HOLDER = new NullHolder(); +public class ConcurrentMapCache extends AbstractValueAdaptingCache { private final String name; private final ConcurrentMap store; - private final boolean allowNullValues; - /** * Create a new ConcurrentMapCache with the specified name. @@ -79,11 +73,11 @@ public class ConcurrentMapCache implements Cache { * (adapting them to an internal null holder value) */ public ConcurrentMapCache(String name, ConcurrentMap store, boolean allowNullValues) { + super(allowNullValues); Assert.notNull(name, "Name must not be null"); Assert.notNull(store, "Store must not be null"); this.name = name; this.store = store; - this.allowNullValues = allowNullValues; } @@ -97,24 +91,9 @@ public class ConcurrentMapCache implements Cache { return this.store; } - public final boolean isAllowNullValues() { - return this.allowNullValues; - } - @Override - public ValueWrapper get(Object key) { - Object value = this.store.get(key); - return toWrapper(value); - } - - @Override - @SuppressWarnings("unchecked") - public T get(Object key, Class type) { - Object value = fromStoreValue(this.store.get(key)); - if (value != null && type != null && !type.isInstance(value)) { - throw new IllegalStateException("Cached value is not of required type [" + type.getName() + "]: " + value); - } - return (T) value; + protected Object lookup(Object key) { + return this.store.get(key); } @Override @@ -125,7 +104,7 @@ public class ConcurrentMapCache implements Cache { @Override public ValueWrapper putIfAbsent(Object key, Object value) { Object existing = this.store.putIfAbsent(key, toStoreValue(value)); - return toWrapper(existing); + return toValueWrapper(existing); } @Override @@ -138,40 +117,4 @@ public class ConcurrentMapCache implements Cache { this.store.clear(); } - - /** - * Convert the given value from the internal store to a user value - * returned from the get method (adapting {@code null}). - * @param storeValue the store value - * @return the value to return to the user - */ - protected Object fromStoreValue(Object storeValue) { - if (this.allowNullValues && storeValue == NULL_HOLDER) { - return null; - } - return storeValue; - } - - /** - * Convert the given user value, as passed into the put method, - * to a value in the internal store (adapting {@code null}). - * @param userValue the given user value - * @return the value to store - */ - protected Object toStoreValue(Object userValue) { - if (this.allowNullValues && userValue == null) { - return NULL_HOLDER; - } - return userValue; - } - - private ValueWrapper toWrapper(Object value) { - return (value != null ? new SimpleValueWrapper(fromStoreValue(value)) : null); - } - - - @SuppressWarnings("serial") - private static class NullHolder implements Serializable { - } - } diff --git a/spring-context/src/main/java/org/springframework/cache/support/AbstractValueAdaptingCache.java b/spring-context/src/main/java/org/springframework/cache/support/AbstractValueAdaptingCache.java new file mode 100644 index 00000000000..82fbd99e83c --- /dev/null +++ b/spring-context/src/main/java/org/springframework/cache/support/AbstractValueAdaptingCache.java @@ -0,0 +1,116 @@ +/* + * Copyright 2002-2015 the original author or authors. + * + * 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.springframework.cache.support; + +import org.springframework.cache.Cache; + +/** + * Common base class for {@link Cache} implementations that need to adapt + * {@code null} values (and potentially other such special values) before + * passing them on to the underlying store. + * + *

Transparently replaces given {@code null} user values with an internal + * {@link NullValue#INSTANCE}, if configured to support {@code null} values + * (as indicated by {@link #isAllowNullValues()}. + * + * @author Juergen Hoeller + * @since 4.2.2 + */ +public abstract class AbstractValueAdaptingCache implements Cache { + + private final boolean allowNullValues; + + + /** + * Create an {@code AbstractValueAdaptingCache} with the given setting. + * @param allowNullValues whether to allow for {@code null} values + */ + protected AbstractValueAdaptingCache(boolean allowNullValues) { + this.allowNullValues = allowNullValues; + } + + + /** + * Return whether {@code null} values are allowed in this cache. + */ + public final boolean isAllowNullValues() { + return this.allowNullValues; + } + + @Override + public ValueWrapper get(Object key) { + Object value = lookup(key); + return toValueWrapper(value); + } + + @Override + @SuppressWarnings("unchecked") + public T get(Object key, Class type) { + Object value = fromStoreValue(lookup(key)); + if (value != null && type != null && !type.isInstance(value)) { + throw new IllegalStateException("Cached value is not of required type [" + type.getName() + "]: " + value); + } + return (T) value; + } + + /** + * Perform an actual lookup in the underlying store. + * @param key the key whose associated value is to be returned + * @return the raw store value for the key + */ + protected abstract Object lookup(Object key); + + + /** + * Convert the given value from the internal store to a user value + * returned from the get method (adapting {@code null}). + * @param storeValue the store value + * @return the value to return to the user + */ + protected Object fromStoreValue(Object storeValue) { + if (this.allowNullValues && storeValue == NullValue.INSTANCE) { + return null; + } + return storeValue; + } + + /** + * Convert the given user value, as passed into the put method, + * to a value in the internal store (adapting {@code null}). + * @param userValue the given user value + * @return the value to store + */ + protected Object toStoreValue(Object userValue) { + if (this.allowNullValues && userValue == null) { + return NullValue.INSTANCE; + } + return userValue; + } + + /** + * Wrap the given store value with a {@link SimpleValueWrapper}, also going + * through {@link #fromStoreValue} conversion. Useful for {@link #get(Object)} + * and {@link #putIfAbsent(Object, Object)} implementations. + * @param storeValue the original value + * @return the wrapped value + */ + protected Cache.ValueWrapper toValueWrapper(Object storeValue) { + return (storeValue != null ? new SimpleValueWrapper(fromStoreValue(storeValue)) : null); + } + + +} diff --git a/spring-context/src/main/java/org/springframework/cache/support/NullValue.java b/spring-context/src/main/java/org/springframework/cache/support/NullValue.java new file mode 100644 index 00000000000..7b3f4678064 --- /dev/null +++ b/spring-context/src/main/java/org/springframework/cache/support/NullValue.java @@ -0,0 +1,43 @@ +/* + * Copyright 2002-2015 the original author or authors. + * + * 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.springframework.cache.support; + +import java.io.Serializable; + +/** + * Simple serializable class that serves as a {@code null} replacement + * for cache stores which otherwise do not support {@code null} values. + * + * @author Juergen Hoeller + * @since 4.2.2 + * @see AbstractValueAdaptingCache + */ +public final class NullValue implements Serializable { + + static final Object INSTANCE = new NullValue(); + + private static final long serialVersionUID = 1L; + + + private NullValue() { + } + + private Object readResolve() { + return INSTANCE; + } + +}