SGF-459 - Add support for the get(key:Object, valueLoader:Callable) method in Spring Framework 4.3's Cache interface.

This commit is contained in:
John Blum
2016-02-01 17:20:12 -08:00
parent 730eea0e38
commit 1b11b912f5
7 changed files with 661 additions and 65 deletions

View File

@@ -0,0 +1,201 @@
/*
* Copyright 2012 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.data.gemfire.support;
import java.util.concurrent.Callable;
import org.springframework.util.Assert;
import com.gemstone.gemfire.cache.CacheLoader;
import com.gemstone.gemfire.cache.CacheLoaderException;
import com.gemstone.gemfire.cache.LoaderHelper;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.TimeoutException;
/**
* The CallableCacheLoaderAdapter class is a {@link Callable} and GemFire {@link CacheLoader} implementation that
* adapts the {@link Callable} interface into an instance of the {@link CacheLoader} interface. This class is useful
* in situations where GemFire developers have several {@link CacheLoader} implementations that they wish to use
* with Spring's Cache Abstraction.
*
* @author John Blum
* @see java.util.concurrent.Callable
* @see com.gemstone.gemfire.cache.CacheLoader
* @see com.gemstone.gemfire.cache.LoaderHelper
* @see com.gemstone.gemfire.cache.Region
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class CallableCacheLoaderAdapter<K, V> implements Callable<V>, CacheLoader<K, V> {
private final K key;
private final CacheLoader<K, V> cacheLoader;
private final Object argument;
private final Region<K, V> region;
/**
* Constructs an instance of the CallableCacheLoaderAdapter that delegates to the given {@link CacheLoader}.
*
* @param delegate the {@link CacheLoader} delegated to by this adapter.
* @see #CallableCacheLoaderAdapter(CacheLoader, Object, Region, Object)
* @see com.gemstone.gemfire.cache.CacheLoader
*/
public CallableCacheLoaderAdapter(CacheLoader<K, V> delegate) {
this(delegate, null, null, null);
}
/**
* Constructs an instance of the CallableCacheLoaderAdapter that delegates to the given {@link CacheLoader}
* and is initialized with the given key for which the value will be loaded along with the {@link Region}
* in which the entry (key/value) belongs.
*
* @param delegate the {@link CacheLoader} delegated to by this adapter.
* @param key the key for which the value will be loaded.
* @param region the {@link Region} in which the entry (key/value) belongs.
* @see #CallableCacheLoaderAdapter(CacheLoader, Object, Region, Object)
* @see com.gemstone.gemfire.cache.CacheLoader
* @see com.gemstone.gemfire.cache.Region
*/
public CallableCacheLoaderAdapter(CacheLoader<K, V> delegate, K key, Region<K, V> region) {
this(delegate, key, region, null);
}
/**
* Constructs an instance of the CallableCacheLoaderAdapter that delegates to the given {@link CacheLoader}
* and is initialized with the given key for which the value will be loaded along with the {@link Region}
* in which the entry (key/value) belongs. Additionally, an argument may be specified for use with the
* {@link CacheLoader} delegate.
*
* @param delegate the {@link CacheLoader} delegated to by this adapter.
* @param key the key for which the value will be loaded.
* @param region the {@link Region} in which the entry (key/value) belongs.
* @param argument the Object argument used with the {@link CacheLoader} delegate.
* @see #CallableCacheLoaderAdapter(CacheLoader, Object, Region, Object)
* @see com.gemstone.gemfire.cache.CacheLoader
* @see com.gemstone.gemfire.cache.Region
*/
public CallableCacheLoaderAdapter(CacheLoader<K, V> delegate, K key, Region<K, V> region, Object argument) {
Assert.notNull(delegate, "CacheLoader must not be null");
this.cacheLoader = delegate;
this.argument = argument;
this.key = key;
this.region = region;
}
/**
* Gets the argument used by this {@link CacheLoader} to load the value for the specified key.
*
* @return an Object argument used by this {@link CacheLoader} when loading the value for the specified key.
*/
protected Object getArgument() {
return argument;
}
/**
* The {@link CacheLoader} delegate used to actually load the cache value for the specified key.
*
* @return a reference to the actual {@link CacheLoader} used when loading the cache value for the specified key.
* @see com.gemstone.gemfire.cache.CacheLoader
*/
protected CacheLoader<K, V> getCacheLoader() {
return cacheLoader;
}
/**
* The specified key for which a value will be loaded by this {@link CacheLoader}.
*
* @return the specified key for which the value will be loaded.
*/
protected K getKey() {
return key;
}
/**
* Returns the Region to which the entry (key/value) belongs.
*
* @return the Region to which the entry belongs.
* @see com.gemstone.gemfire.cache.Region
*/
protected Region<K, V> getRegion() {
return region;
}
/**
* Invoked to load a cache value for the specified key. Delegates to {@link #load(LoaderHelper)}.
*
* @return the loaded cache value for the specified key.
* @throws java.lang.IllegalStateException if the {@link Region} or key references are null.
* @throws java.lang.Exception if the load operation fails.
* @see #load(LoaderHelper)
*/
public final V call() throws Exception {
Assert.state(getKey() != null, "The key for which the value is loaded for cannot be null");
Assert.state(getRegion() != null, "The Region to load cannot be null");
return load(new LoaderHelper<K, V>() {
public V netSearch(final boolean doNetLoad) throws CacheLoaderException, TimeoutException {
throw new UnsupportedOperationException("not implemented");
}
public K getKey() {
return CallableCacheLoaderAdapter.this.getKey();
}
public Region<K, V> getRegion() {
return CallableCacheLoaderAdapter.this.getRegion();
}
public Object getArgument() {
return CallableCacheLoaderAdapter.this.getArgument();
}
});
}
/**
* Closes any resources used by this {@link CacheLoader}. Delegates to the underlying {@link CacheLoader}.
*
* @see #getCacheLoader()
*/
public void close() {
getCacheLoader().close();
}
/**
* Loads a value for the specified cache (i.e. {@link Region}) and key with the help of the {@link LoaderHelper}.
* Delegates to the underlying {@link CacheLoader}.
*
* @param loaderHelper a {@link LoaderHelper} object passed in from cache service providing access to the key,
* {@link Region}, argument, and <code>netSearch</code>.
* @return the value supplied for the specified key, or null if no value can be supplied. A local loader will
* always be invoked if one exists. Otherwise one remote loader is invoked. Returning <code>null</code> causes
* {@link Region#get(Object, Object)} to return <code>null</code>.
* @throws CacheLoaderException if an error occurs during the load operation. This exception, or any other
* Exception thrown by this method will be propagated back to the application from the
* {@link Region#get(Object)} method.
* @see com.gemstone.gemfire.cache.CacheLoader#load(LoaderHelper)
* @see com.gemstone.gemfire.cache.LoaderHelper
* @see #getCacheLoader()
*/
public V load(LoaderHelper<K, V> loaderHelper) throws CacheLoaderException {
return getCacheLoader().load(loaderHelper);
}
}

View File

@@ -16,8 +16,11 @@
package org.springframework.data.gemfire.support;
import java.util.concurrent.Callable;
import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.util.ObjectUtils;
import com.gemstone.gemfire.cache.GemFireCache;
import com.gemstone.gemfire.cache.Region;
@@ -81,6 +84,33 @@ public class GemfireCache implements Cache {
return (T) value;
}
@SuppressWarnings("unchecked")
public <T> T get(final Object key, final Callable<T> valueLoader) {
T value = (T) get(key, Object.class);
if (value == null) {
synchronized (region) {
value = (T) get(key, Object.class);
if (value == null) {
try {
value = valueLoader.call();
put(key, value);
}
catch (Exception e) {
throw new RuntimeException(String.format(
"Failed to load value for key [%1$s] using valueLoader [%2$s]", key,
ObjectUtils.nullSafeClassName(valueLoader)));
//TODO throw ValueRetrievalException when SDG is based on Spring Framework 4.3
//throw new ValueRetrievalException(key, valueLoader, e);
}
}
}
}
return value;
}
@SuppressWarnings("unchecked")
public void put(final Object key, final Object value) {
if (value != null) {