Javadoc'd almost everything
This commit is contained in:
@@ -20,13 +20,26 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* Specify the bucket in which to store the annotated object, overriding the
|
||||
* default classname method of deriving bucket name.
|
||||
*
|
||||
* @author J. Brisbin <jon@jbrisbin.com>
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface KeyValueStoreMetaData {
|
||||
|
||||
/**
|
||||
* The bucket in which to store an instance of this object.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String bucket();
|
||||
|
||||
/**
|
||||
* The media type in which to covert and store this object.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String mediaType() default "application/json";
|
||||
|
||||
}
|
||||
|
||||
@@ -1,12 +1,25 @@
|
||||
package org.springframework.datastore.riak.core;
|
||||
|
||||
/**
|
||||
* A generic interface for representing composite keys in data stores that use a
|
||||
* bucket and key pair.
|
||||
*
|
||||
* @author J. Brisbin <jon@jbrisbin.com>
|
||||
*/
|
||||
public interface BucketKeyPair<B, K> {
|
||||
|
||||
/**
|
||||
* Get the bucket representation.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
B getBucket();
|
||||
|
||||
/**
|
||||
* Get the key representation.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
K getKey();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,27 @@
|
||||
package org.springframework.datastore.riak.core;
|
||||
|
||||
/**
|
||||
* A generic interface to a resolver to turn a single object into a {@link
|
||||
* org.springframework.datastore.riak.core.BucketKeyPair}.
|
||||
*
|
||||
* @author J. Brisbin <jon@jbrisbin.com>
|
||||
*/
|
||||
public interface BucketKeyResolver {
|
||||
|
||||
/**
|
||||
* Can this resolver deal with the given object?
|
||||
*
|
||||
* @param o
|
||||
* @param <V>
|
||||
* @return
|
||||
*/
|
||||
<V> boolean canResolve(V o);
|
||||
|
||||
/**
|
||||
* Turn the given object into a BucketKeyPair.
|
||||
*
|
||||
* @param o
|
||||
* @return
|
||||
*/
|
||||
<B, K, V> BucketKeyPair<B, K> resolve(V o);
|
||||
}
|
||||
|
||||
@@ -5,12 +5,24 @@ import org.springframework.http.MediaType;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A generic interface to MetaData provided by Key/Value data stores.
|
||||
*
|
||||
* @author J. Brisbin <jon@jbrisbin.com>
|
||||
*/
|
||||
public interface KeyValueStoreMetaData {
|
||||
|
||||
/**
|
||||
* Get the Content-Type of this object.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
MediaType getContentType();
|
||||
|
||||
/**
|
||||
* Get the arbitrary properties for this object.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Map<String, Object> getProperties();
|
||||
|
||||
}
|
||||
|
||||
@@ -18,57 +18,232 @@ package org.springframework.datastore.riak.core;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Generic abstraction for Key/Value stores. Contains most operations that
|
||||
* generic K/V stores might expose.
|
||||
*/
|
||||
public interface KeyValueStoreOperations {
|
||||
|
||||
// Set and Set with expiry operations
|
||||
// Set operations
|
||||
|
||||
/**
|
||||
* Set a value at a specified key.
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
* @return This template interface
|
||||
*/
|
||||
<K, V> KeyValueStoreOperations set(K key, V value);
|
||||
|
||||
/**
|
||||
* Set a value as a byte array at a specified key.
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
* @return This template interface
|
||||
*/
|
||||
<K> KeyValueStoreOperations setAsBytes(K key, byte[] value);
|
||||
|
||||
// Get operations
|
||||
|
||||
/**
|
||||
* Get a value at the specified key, trying to infer the type from either the
|
||||
* bucket in which the value was stored, or (by default) as a
|
||||
* <code>java.util.Map</code>.
|
||||
*
|
||||
* @param key
|
||||
* @return The converted value, or <code>null</code> if not found.
|
||||
*/
|
||||
<K, V> V get(K key);
|
||||
|
||||
/**
|
||||
* Get the value at the specified key as a byte array.
|
||||
*
|
||||
* @param key
|
||||
* @return The byte array of data, or <code>null</code> if not found.
|
||||
*/
|
||||
<K> byte[] getAsBytes(K key);
|
||||
|
||||
/**
|
||||
* Get the value at the specified key and convert it into an instance of the
|
||||
* specified type.
|
||||
*
|
||||
* @param key
|
||||
* @param requiredType
|
||||
* @return The converted value, or <code>null</code> if not found.
|
||||
*/
|
||||
<K, T> T getAsType(K key, Class<T> requiredType);
|
||||
|
||||
// Get and Set operations
|
||||
|
||||
/**
|
||||
* Get the old value at the specified key and replace it with the given
|
||||
* value.
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
* @return The old value (before it was overwritten).
|
||||
*/
|
||||
<K, V> V getAndSet(K key, V value);
|
||||
|
||||
/**
|
||||
* Get the old value at the specified key as a byte array and replace it with
|
||||
* the given bytes.
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
* @return The old byte array (before it was overwritten).
|
||||
*/
|
||||
<K> byte[] getAndSetAsBytes(K key, byte[] value);
|
||||
|
||||
/**
|
||||
* Get the old value at the specified key and replace it with the given value,
|
||||
* converting it to an instance of the given type.
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
* @param requiredType The type to convert the value to.
|
||||
* @return The old value (before it was overwritten).
|
||||
*/
|
||||
<K, V, T> T getAndSetAsType(K key, V value, Class<T> requiredType);
|
||||
|
||||
// Multi-get operations
|
||||
|
||||
/**
|
||||
* Get all the values at the specified keys.
|
||||
*
|
||||
* @param keys
|
||||
* @return A list of the values retrieved or an empty list if none were
|
||||
* found.
|
||||
*/
|
||||
<K, V> List<V> getValues(List<K> keys);
|
||||
|
||||
/**
|
||||
* Variation on {@link KeyValueStoreOperations#getValues(java.util.List)} that
|
||||
* uses varargs instead of a <code>java.util.List</code>.
|
||||
*
|
||||
* @param keys
|
||||
* @return A list of the values retrieved or an empty list if none were
|
||||
* found.
|
||||
*/
|
||||
<K, V> List<V> getValues(K... keys);
|
||||
|
||||
/**
|
||||
* Get all the values at the specified keys, converting the values into
|
||||
* instances of the specified type.
|
||||
*
|
||||
* @param keys
|
||||
* @param requiredType
|
||||
* @return A list of the values retrieved or an empty list if none were
|
||||
* found.
|
||||
*/
|
||||
<K, T> List<T> getValuesAsType(List<K> keys, Class<T> requiredType);
|
||||
|
||||
/**
|
||||
* A variation on {@link KeyValueStoreOperations#getValuesAsType(java.util.List,
|
||||
* Class)} that takes uses varargs instead of a <code>java.util.List</code>.
|
||||
*
|
||||
* @param requiredType
|
||||
* @param keys
|
||||
* @return A list of the values retrieved or an empty list if none were
|
||||
* found.
|
||||
*/
|
||||
<T, K> List<T> getValuesAsType(Class<T> requiredType, K... keys);
|
||||
|
||||
// Set if non-existent operations
|
||||
|
||||
/**
|
||||
* Set the value at the given key only if that key doesn't already exist.
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
* @return This template interface
|
||||
*/
|
||||
<K, V> KeyValueStoreOperations setIfKeyNonExistent(K key, V value);
|
||||
|
||||
/**
|
||||
* Set the value at the given key as a byte array only if that key doesn't
|
||||
* already exist.
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
* @return This template interface
|
||||
*/
|
||||
<K> KeyValueStoreOperations setIfKeyNonExistentAsBytes(K key, byte[] value);
|
||||
|
||||
// Multiple key-value set
|
||||
|
||||
/**
|
||||
* Convenience method to set multiple values as Key/Value pairs.
|
||||
*
|
||||
* @param keysAndValues
|
||||
* @return This template interface
|
||||
*/
|
||||
<K, V> KeyValueStoreOperations setMultiple(Map<K, V> keysAndValues);
|
||||
|
||||
/**
|
||||
* Convenience method to set multiple values as Key/byte[] pairs.
|
||||
*
|
||||
* @param keysAndValues
|
||||
* @return This template interface
|
||||
*/
|
||||
<K> KeyValueStoreOperations setMultipleAsBytes(Map<K, byte[]> keysAndValues);
|
||||
|
||||
// Multiple key-value set if non-existent
|
||||
|
||||
/**
|
||||
* Variation on setting multiple values only if the key doesn't already
|
||||
* exist.
|
||||
*
|
||||
* @param keysAndValues
|
||||
* @return This template interface
|
||||
*/
|
||||
<K, V> KeyValueStoreOperations setMultipleIfKeysNonExistent(Map<K, V> keysAndValues);
|
||||
|
||||
/**
|
||||
* Variation on setting multiple values as byte arryas only if the key doesn't
|
||||
* already exist.
|
||||
*
|
||||
* @param keysAndValues
|
||||
* @param <K>
|
||||
* @return
|
||||
*/
|
||||
<K> KeyValueStoreOperations setMultipleAsBytesIfKeysNonExistent(Map<K, byte[]> keysAndValues);
|
||||
|
||||
/**
|
||||
* Does the store contain this key?
|
||||
*
|
||||
* @param key
|
||||
* @return <code>true</code> if the key exists, <code>false</code> otherwise.
|
||||
*/
|
||||
<K> boolean containsKey(K key);
|
||||
|
||||
/**
|
||||
* Delete one or more keys from the store.
|
||||
*
|
||||
* @param keys
|
||||
* @return <code>true</code> if all keys were successfully deleted,
|
||||
* <code>false</code> otherwise.
|
||||
*/
|
||||
<K> boolean deleteKeys(K... keys);
|
||||
|
||||
/**
|
||||
* Get the properties of the specified bucket.
|
||||
*
|
||||
* @param bucket
|
||||
* @return The bucket properties, without a list of keys in that bucket.
|
||||
*/
|
||||
<B> Map<String, Object> getBucketSchema(B bucket);
|
||||
|
||||
/**
|
||||
* Get the properties of the bucket and specify whether or not to list the
|
||||
* keys in that bucket.
|
||||
*
|
||||
* @param bucket
|
||||
* @param listKeys
|
||||
* @return The bucket properties, with or without a list of keys in that
|
||||
* bucket.
|
||||
*/
|
||||
<B> Map<String, Object> getBucketSchema(B bucket, boolean listKeys);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,12 +1,24 @@
|
||||
package org.springframework.datastore.riak.core;
|
||||
|
||||
/**
|
||||
* A generic interface for dealing with values and their store metadata.
|
||||
*
|
||||
* @author J. Brisbin <jon@jbrisbin.com>
|
||||
*/
|
||||
public interface KeyValueStoreValue<T> {
|
||||
|
||||
/**
|
||||
* Get the metadata associated with this value.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
KeyValueStoreMetaData getMetaData();
|
||||
|
||||
/**
|
||||
* Get the converted value itself.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
T get();
|
||||
|
||||
}
|
||||
|
||||
@@ -5,6 +5,9 @@ import org.springframework.http.MediaType;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* An implementation of {@link org.springframework.datastore.riak.core.KeyValueStoreMetaData}
|
||||
* for Riak.
|
||||
*
|
||||
* @author J. Brisbin <jon@jbrisbin.com>
|
||||
*/
|
||||
public class RiakMetaData implements KeyValueStoreMetaData {
|
||||
|
||||
@@ -61,41 +61,112 @@ import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* An implementation of {@link org.springframework.datastore.riak.core.KeyValueStoreOperations}
|
||||
* and {@link org.springframework.datastore.riak.mapreduce.MapReduceOperations}
|
||||
* for the Riak data store.
|
||||
* <p/>
|
||||
* To use the RiakTemplate, create a singleton in your Spring
|
||||
* application-context.xml:
|
||||
* <pre><code>
|
||||
* <bean id="riak" class="org.springframework.datastore.riak.core.RiakTemplate"
|
||||
* p:defaultUri="http://localhost:8098/riak/{bucket}/{key}"
|
||||
* p:mapReduceUri="http://localhost:8098/mapred"/>
|
||||
* </code></pre>
|
||||
* To store and retrieve objects in Riak, use the setXXX and getXXX methods
|
||||
* (example in Groovy):
|
||||
* <pre><code>
|
||||
* def obj = new TestObject(name: "My Name", age: 40)
|
||||
* riak.set([bucket: "mybucket", key: "mykey"], obj)
|
||||
* ...
|
||||
* def name = riak.get([bucket: "mybucket", key: "mykey"]).name
|
||||
* println "Hello $name!"
|
||||
* </code></pre>
|
||||
* You're key object should be one of: <ul><li>A <code>String</code> encoding
|
||||
* the bucket and key together, separated by a colon. e.g. "mybucket:mykey"</li>
|
||||
* <li>An implementation of BucketKeyPair (like {@link org.springframework.datastore.riak.core.SimpleBucketKeyPair})</li>
|
||||
* <li>A <code>Map</code> with both a "bucket" and a "key" specified.</li> <li>A
|
||||
* <code>String</code> of only the key name, but specifying a bucket by using
|
||||
* the {@link org.springframework.datastore.riak.convert.KeyValueStoreMetaData}
|
||||
* annotation on the object you're storing.</li></ul>
|
||||
*
|
||||
* @author J. Brisbin <jon@jbrisbin.com>
|
||||
*/
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public class RiakTemplate extends RestGatewaySupport implements KeyValueStoreOperations, MapReduceOperations, InitializingBean {
|
||||
|
||||
/**
|
||||
* Client ID used by Riak to correlate updates.
|
||||
*/
|
||||
private static final String RIAK_CLIENT_ID = "org.springframework.datastore.riak.core.RiakTemplate/1.0";
|
||||
private static final Pattern prefix = Pattern.compile("http[s]?://(\\S+):([0-9]+)/(\\S+)/\\{bucket\\}(\\S+)");
|
||||
private static final boolean groovyPresent = ClassUtils.isPresent("org.codehaus.groovy.runtime.GStringImpl",
|
||||
/**
|
||||
* Regex used to extract host, port, and prefix from the given URI.
|
||||
*/
|
||||
private static final Pattern prefix = Pattern.compile(
|
||||
"http[s]?://(\\S+):([0-9]+)/(\\S+)/\\{bucket\\}(\\S+)");
|
||||
/**
|
||||
* Do we need to handle Groovy strings in the Jackson JSON processor?
|
||||
*/
|
||||
private static final boolean groovyPresent = ClassUtils.isPresent(
|
||||
"org.codehaus.groovy.runtime.GStringImpl",
|
||||
RiakTemplate.class.getClassLoader());
|
||||
|
||||
private static SimpleDateFormat httpDate = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z");
|
||||
/**
|
||||
* For getting a <code>java.util.Date</code> from the Last-Modified header.
|
||||
*/
|
||||
private static SimpleDateFormat httpDate = new SimpleDateFormat(
|
||||
"EEE, d MMM yyyy HH:mm:ss z");
|
||||
|
||||
protected final Logger log = LoggerFactory.getLogger(getClass());
|
||||
/**
|
||||
* For converting objects to/from other kinds of objects.
|
||||
*/
|
||||
protected ConversionService conversionService = ConversionServiceFactory.createDefaultConversionService();
|
||||
/**
|
||||
* For caching objects based on ETags.
|
||||
*/
|
||||
protected ConcurrentSkipListMap<BucketKeyPair, RiakValue<?>> cache = new ConcurrentSkipListMap<BucketKeyPair, RiakValue<?>>();
|
||||
/**
|
||||
* Whether or not to use the ETag-based cache.
|
||||
*/
|
||||
protected boolean useCache = true;
|
||||
/**
|
||||
* Not yet used.
|
||||
*/
|
||||
protected ExecutorService queue = Executors.newCachedThreadPool();
|
||||
|
||||
/**
|
||||
* The URI to use inside the RestTemplate.
|
||||
*/
|
||||
protected String defaultUri = "http://localhost:8098/riak/{bucket}/{key}";
|
||||
/**
|
||||
* The URI for the Riak Map/Reduce API.
|
||||
*/
|
||||
protected String mapReduceUri = "http://localhost:8098/mapred";
|
||||
/**
|
||||
* A list of resolvers to turn a single object into a {#link BucketKeyPair}.
|
||||
*/
|
||||
protected List<BucketKeyResolver> bucketKeyResolvers;
|
||||
|
||||
/**
|
||||
* Take all the defaults.
|
||||
*/
|
||||
public RiakTemplate() {
|
||||
setRestTemplate(new RestTemplate());
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the specified {@link org.springframework.http.client.ClientHttpRequestFactory}.
|
||||
*
|
||||
* @param requestFactory
|
||||
*/
|
||||
public RiakTemplate(ClientHttpRequestFactory requestFactory) {
|
||||
super(requestFactory);
|
||||
}
|
||||
|
||||
public RiakTemplate(String defaultUri) {
|
||||
setRestTemplate(new RestTemplate());
|
||||
setDefaultUri(defaultUri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the specified defaultUri and mapReduceUri.
|
||||
*
|
||||
* @param defaultUri
|
||||
* @param mapReduceUri
|
||||
*/
|
||||
public RiakTemplate(String defaultUri, String mapReduceUri) {
|
||||
setRestTemplate(new RestTemplate());
|
||||
this.setDefaultUri(defaultUri);
|
||||
@@ -106,6 +177,11 @@ public class RiakTemplate extends RestGatewaySupport implements KeyValueStoreOpe
|
||||
return conversionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the conversion service to use.
|
||||
*
|
||||
* @param conversionService
|
||||
*/
|
||||
public void setConversionService(ConversionService conversionService) {
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
@@ -130,6 +206,11 @@ public class RiakTemplate extends RestGatewaySupport implements KeyValueStoreOpe
|
||||
return bucketKeyResolvers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the list of BucketKeyResolvers to use.
|
||||
*
|
||||
* @param bucketKeyResolvers
|
||||
*/
|
||||
public void setBucketKeyResolvers(List<BucketKeyResolver> bucketKeyResolvers) {
|
||||
this.bucketKeyResolvers = bucketKeyResolvers;
|
||||
}
|
||||
@@ -142,6 +223,14 @@ public class RiakTemplate extends RestGatewaySupport implements KeyValueStoreOpe
|
||||
this.useCache = useCache;
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
Matcher m = prefix.matcher(defaultUri);
|
||||
if (m.matches()) {
|
||||
return "/" + m.group(3);
|
||||
}
|
||||
return "/riak";
|
||||
}
|
||||
|
||||
/*----------------- Set Operations -----------------*/
|
||||
|
||||
public <K, V> KeyValueStoreOperations set(K key, V value) {
|
||||
@@ -151,7 +240,8 @@ public class RiakTemplate extends RestGatewaySupport implements KeyValueStoreOpe
|
||||
public <K> KeyValueStoreOperations setAsBytes(K key, byte[] value) {
|
||||
Assert.notNull(key, "Can't store an object with a NULL key.");
|
||||
BucketKeyPair bucketKeyPair = resolveBucketKeyPair(key, value);
|
||||
String bucketName = (null != bucketKeyPair.getBucket() ? bucketKeyPair.getBucket().toString() : "bytes");
|
||||
String bucketName = (null != bucketKeyPair.getBucket() ? bucketKeyPair.getBucket()
|
||||
.toString() : "bytes");
|
||||
RestTemplate restTemplate = getRestTemplate();
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("X-Riak-ClientId", RIAK_CLIENT_ID);
|
||||
@@ -159,7 +249,9 @@ public class RiakTemplate extends RestGatewaySupport implements KeyValueStoreOpe
|
||||
HttpEntity<byte[]> entity = new HttpEntity<byte[]>(value, headers);
|
||||
restTemplate.put(defaultUri, entity, bucketName, bucketKeyPair.getKey());
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(String.format("PUT byte[]: bucket=%s, key=%s", bucketKeyPair.getBucket(), bucketKeyPair.getKey()));
|
||||
log.debug(String.format("PUT byte[]: bucket=%s, key=%s",
|
||||
bucketKeyPair.getBucket(),
|
||||
bucketKeyPair.getKey()));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@@ -176,7 +268,10 @@ public class RiakTemplate extends RestGatewaySupport implements KeyValueStoreOpe
|
||||
}
|
||||
}
|
||||
HttpEntity<V> entity = new HttpEntity<V>(value, headers);
|
||||
restTemplate.put(defaultUri, entity, bucketKeyPair.getBucket(), bucketKeyPair.getKey());
|
||||
restTemplate.put(defaultUri,
|
||||
entity,
|
||||
bucketKeyPair.getBucket(),
|
||||
bucketKeyPair.getKey());
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(String.format("PUT object: bucket=%s, key=%s, value=%s",
|
||||
bucketKeyPair.getBucket(),
|
||||
@@ -250,27 +345,32 @@ public class RiakTemplate extends RestGatewaySupport implements KeyValueStoreOpe
|
||||
}
|
||||
|
||||
try {
|
||||
RiakValue<byte[]> bytes = (RiakValue<byte[]>) restTemplate.execute(defaultUri,
|
||||
RiakValue<byte[]> bytes = (RiakValue<byte[]>) restTemplate.execute(
|
||||
defaultUri,
|
||||
HttpMethod.GET,
|
||||
new RequestCallback() {
|
||||
public void doWithRequest(ClientHttpRequest request) throws IOException {
|
||||
public void doWithRequest(ClientHttpRequest request) throws
|
||||
IOException {
|
||||
List<MediaType> mediaTypes = new ArrayList<MediaType>();
|
||||
mediaTypes.add(MediaType.APPLICATION_JSON);
|
||||
request.getHeaders().setAccept(mediaTypes);
|
||||
}
|
||||
},
|
||||
new ResponseExtractor<Object>() {
|
||||
public Object extractData(ClientHttpResponse response) throws IOException {
|
||||
public Object extractData(ClientHttpResponse response) throws
|
||||
IOException {
|
||||
InputStream in = response.getBody();
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
byte[] buff = new byte[in.available()];
|
||||
for (int bytesRead = in.read(buff); bytesRead > 0; bytesRead = in.read(buff)) {
|
||||
for (int bytesRead = in.read(buff); bytesRead > 0; bytesRead = in.read(
|
||||
buff)) {
|
||||
out.write(buff, 0, bytesRead);
|
||||
}
|
||||
|
||||
HttpHeaders headers = response.getHeaders();
|
||||
RiakMetaData meta = extractMetaData(headers);
|
||||
RiakValue<byte[]> val = new RiakValue<byte[]>(out.toByteArray(), meta);
|
||||
RiakValue<byte[]> val = new RiakValue<byte[]>(out.toByteArray(),
|
||||
meta);
|
||||
return val;
|
||||
}
|
||||
},
|
||||
@@ -351,7 +451,9 @@ public class RiakTemplate extends RestGatewaySupport implements KeyValueStoreOpe
|
||||
set(key, value);
|
||||
} else {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(String.format("key: %s already exists. Not adding %s", key, value));
|
||||
log.debug(String.format("key: %s already exists. Not adding %s",
|
||||
key,
|
||||
value));
|
||||
}
|
||||
}
|
||||
return this;
|
||||
@@ -362,7 +464,9 @@ public class RiakTemplate extends RestGatewaySupport implements KeyValueStoreOpe
|
||||
setAsBytes(key, value);
|
||||
} else {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(String.format("key: %s already exists. Not adding %s", key, value));
|
||||
log.debug(String.format("key: %s already exists. Not adding %s",
|
||||
key,
|
||||
value));
|
||||
}
|
||||
}
|
||||
return this;
|
||||
@@ -405,7 +509,9 @@ public class RiakTemplate extends RestGatewaySupport implements KeyValueStoreOpe
|
||||
RestTemplate restTemplate = getRestTemplate();
|
||||
HttpHeaders headers = null;
|
||||
try {
|
||||
headers = restTemplate.headForHeaders(defaultUri, bucketKeyPair.getBucket(), bucketKeyPair.getKey());
|
||||
headers = restTemplate.headForHeaders(defaultUri,
|
||||
bucketKeyPair.getBucket(),
|
||||
bucketKeyPair.getKey());
|
||||
} catch (ResourceAccessException e) {
|
||||
}
|
||||
return (null != headers);
|
||||
@@ -442,7 +548,9 @@ public class RiakTemplate extends RestGatewaySupport implements KeyValueStoreOpe
|
||||
|
||||
public <T> T execute(MapReduceJob job, Class<T> targetType) {
|
||||
RestTemplate restTemplate = getRestTemplate();
|
||||
ResponseEntity<T> resp = restTemplate.postForEntity(mapReduceUri, job.toJson(), targetType);
|
||||
ResponseEntity<T> resp = restTemplate.postForEntity(mapReduceUri,
|
||||
job.toJson(),
|
||||
targetType);
|
||||
if (resp.hasBody()) {
|
||||
return resp.getBody();
|
||||
}
|
||||
@@ -455,6 +563,14 @@ public class RiakTemplate extends RestGatewaySupport implements KeyValueStoreOpe
|
||||
|
||||
/*----------------- Link Operations -----------------*/
|
||||
|
||||
/**
|
||||
* Use Riak's native Link mechanism to link two entries together.
|
||||
*
|
||||
* @param destination Key to the child object
|
||||
* @param source Key to the parent object
|
||||
* @param tag The tag for this relationship
|
||||
* @return This template interface
|
||||
*/
|
||||
public <K1, K2> RiakTemplate link(K1 destination, K2 source, String tag) {
|
||||
BucketKeyPair bkpFrom = resolveBucketKeyPair(source, null);
|
||||
BucketKeyPair bkpTo = resolveBucketKeyPair(destination, null);
|
||||
@@ -462,7 +578,8 @@ public class RiakTemplate extends RestGatewaySupport implements KeyValueStoreOpe
|
||||
|
||||
RiakValue<byte[]> fromObj = getAsBytesWithMetaData(source);
|
||||
if (null == fromObj) {
|
||||
throw new DataStoreOperationException("Cannot link from a non-existent source: " + source);
|
||||
throw new DataStoreOperationException(
|
||||
"Cannot link from a non-existent source: " + source);
|
||||
}
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(fromObj.getMetaData().getContentType());
|
||||
@@ -473,7 +590,11 @@ public class RiakTemplate extends RestGatewaySupport implements KeyValueStoreOpe
|
||||
} else if (linksObj instanceof String) {
|
||||
links.add(linksObj.toString());
|
||||
}
|
||||
links.add(String.format("<%s/%s/%s>; riaktag=\"%s\"", extractPrefix(), bkpTo.getBucket(), bkpTo.getKey(), tag));
|
||||
links.add(String.format("<%s/%s/%s>; riaktag=\"%s\"",
|
||||
getPrefix(),
|
||||
bkpTo.getBucket(),
|
||||
bkpTo.getKey(),
|
||||
tag));
|
||||
StringWriter sw = new StringWriter();
|
||||
boolean needsComma = false;
|
||||
for (String link : links) {
|
||||
@@ -493,21 +614,34 @@ public class RiakTemplate extends RestGatewaySupport implements KeyValueStoreOpe
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Incomplete implementation of Link Walking.
|
||||
*
|
||||
* @param source
|
||||
* @param tag
|
||||
* @return
|
||||
*/
|
||||
public <T, K> T linkWalk(K source, String tag) {
|
||||
BucketKeyPair bkpSource = resolveBucketKeyPair(source, null);
|
||||
RestTemplate restTemplate = getRestTemplate();
|
||||
final List<MediaType> types = new ArrayList<MediaType>();
|
||||
types.add(MediaType.ALL);
|
||||
restTemplate.execute(defaultUri + "/_,{tag},_", HttpMethod.GET, new RequestCallback() {
|
||||
public void doWithRequest(ClientHttpRequest request) throws IOException {
|
||||
request.getHeaders().setAccept(types);
|
||||
}
|
||||
}, new ResponseExtractor<Object>() {
|
||||
public Object extractData(ClientHttpResponse response) throws IOException {
|
||||
response.getHeaders();
|
||||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
}, bkpSource.getBucket(),
|
||||
restTemplate.execute(defaultUri + "/_,{tag},_",
|
||||
HttpMethod.GET,
|
||||
new RequestCallback() {
|
||||
public void doWithRequest(ClientHttpRequest request) throws
|
||||
IOException {
|
||||
request.getHeaders().setAccept(types);
|
||||
}
|
||||
},
|
||||
new ResponseExtractor<Object>() {
|
||||
public Object extractData(ClientHttpResponse response) throws
|
||||
IOException {
|
||||
response.getHeaders();
|
||||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
},
|
||||
bkpSource.getBucket(),
|
||||
bkpSource.getKey(),
|
||||
tag);
|
||||
return null;
|
||||
@@ -528,12 +662,14 @@ public class RiakTemplate extends RestGatewaySupport implements KeyValueStoreOpe
|
||||
if (resp.hasBody()) {
|
||||
return resp.getBody();
|
||||
} else {
|
||||
throw new DataStoreOperationException("Error encountered retrieving bucket schema (Status: " + resp.getStatusCode() + ")");
|
||||
throw new DataStoreOperationException(
|
||||
"Error encountered retrieving bucket schema (Status: " + resp.getStatusCode() + ")");
|
||||
}
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(conversionService, "Must specify a valid ConversionService.");
|
||||
Assert.notNull(conversionService,
|
||||
"Must specify a valid ConversionService.");
|
||||
if (null == bucketKeyResolvers) {
|
||||
bucketKeyResolvers = new ArrayList<BucketKeyResolver>();
|
||||
bucketKeyResolvers.add(new SimpleBucketKeyResolver());
|
||||
@@ -548,7 +684,8 @@ public class RiakTemplate extends RestGatewaySupport implements KeyValueStoreOpe
|
||||
mapper.setSerializerFactory(fac);
|
||||
for (HttpMessageConverter converter : converters) {
|
||||
if (converter instanceof MappingJacksonHttpMessageConverter) {
|
||||
((MappingJacksonHttpMessageConverter) converter).setObjectMapper(mapper);
|
||||
((MappingJacksonHttpMessageConverter) converter).setObjectMapper(
|
||||
mapper);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -569,24 +706,28 @@ public class RiakTemplate extends RestGatewaySupport implements KeyValueStoreOpe
|
||||
if (null != resolver) {
|
||||
bucketKeyPair = resolver.resolve(key);
|
||||
if (null != val) {
|
||||
Annotation meta = (val instanceof Class ? (Class) val : val.getClass()).getAnnotation(KeyValueStoreMetaData.class);
|
||||
Annotation meta = (val instanceof Class ? (Class) val : val.getClass()).getAnnotation(
|
||||
KeyValueStoreMetaData.class);
|
||||
if (null != meta) {
|
||||
String bucket = ((KeyValueStoreMetaData) meta).bucket();
|
||||
if (null != bucket) {
|
||||
return new SimpleBucketKeyPair<String, Object>(bucket, bucketKeyPair.getKey());
|
||||
return new SimpleBucketKeyPair<String, Object>(bucket,
|
||||
bucketKeyPair.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
return bucketKeyPair;
|
||||
}
|
||||
throw new DataStoreOperationException(String.format("No resolvers available to resolve bucket/key pair from %s",
|
||||
throw new DataStoreOperationException(String.format(
|
||||
"No resolvers available to resolve bucket/key pair from %s",
|
||||
key));
|
||||
}
|
||||
|
||||
protected MediaType extractMediaType(Object value) {
|
||||
MediaType mediaType = (value instanceof byte[] ? MediaType.APPLICATION_OCTET_STREAM : MediaType.APPLICATION_JSON);
|
||||
if (value.getClass().getAnnotations().length > 0) {
|
||||
KeyValueStoreMetaData meta = value.getClass().getAnnotation(KeyValueStoreMetaData.class);
|
||||
KeyValueStoreMetaData meta = value.getClass()
|
||||
.getAnnotation(KeyValueStoreMetaData.class);
|
||||
if (null != meta) {
|
||||
mediaType = MediaType.parseMediaType(meta.mediaType());
|
||||
}
|
||||
@@ -594,13 +735,15 @@ public class RiakTemplate extends RestGatewaySupport implements KeyValueStoreOpe
|
||||
return mediaType;
|
||||
}
|
||||
|
||||
protected RiakMetaData extractMetaData(HttpHeaders headers) throws IOException {
|
||||
protected RiakMetaData extractMetaData(HttpHeaders headers) throws
|
||||
IOException {
|
||||
Map<String, Object> props = new LinkedHashMap<String, Object>();
|
||||
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
|
||||
List<String> val = entry.getValue();
|
||||
Object prop = (1 == val.size() ? val.get(0) : val);
|
||||
try {
|
||||
if (entry.getKey().equals("Last-Modified") || entry.getKey().equals("Date")) {
|
||||
if (entry.getKey().equals("Last-Modified") || entry.getKey()
|
||||
.equals("Date")) {
|
||||
prop = httpDate.parse(val.get(0));
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
@@ -637,8 +780,14 @@ public class RiakTemplate extends RestGatewaySupport implements KeyValueStoreOpe
|
||||
String bucketName = (null != bucketKeyPair.getBucket() ? bucketKeyPair.getBucket()
|
||||
.toString() : requiredType.getName());
|
||||
RestTemplate restTemplate = getRestTemplate();
|
||||
HttpHeaders resp = restTemplate.headForHeaders(defaultUri, bucketName, bucketKeyPair.getKey());
|
||||
if (!obj.getMetaData().getProperties().get("ETag").toString().equals(resp.getETag())) {
|
||||
HttpHeaders resp = restTemplate.headForHeaders(defaultUri,
|
||||
bucketName,
|
||||
bucketKeyPair.getKey());
|
||||
if (!obj.getMetaData()
|
||||
.getProperties()
|
||||
.get("ETag")
|
||||
.toString()
|
||||
.equals(resp.getETag())) {
|
||||
obj = null;
|
||||
} else {
|
||||
if (log.isDebugEnabled()) {
|
||||
@@ -649,12 +798,4 @@ public class RiakTemplate extends RestGatewaySupport implements KeyValueStoreOpe
|
||||
return (null != obj ? (T) obj.get() : null);
|
||||
}
|
||||
|
||||
public String extractPrefix() {
|
||||
Matcher m = prefix.matcher(defaultUri);
|
||||
if (m.matches()) {
|
||||
return "/" + m.group(3);
|
||||
}
|
||||
return "/riak";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ import java.util.regex.Pattern;
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public class SimpleBucketKeyResolver implements BucketKeyResolver {
|
||||
|
||||
private static final boolean groovyPresent = ClassUtils.isPresent("org.codehaus.groovy.runtime.GStringImpl",
|
||||
private static final boolean groovyPresent = ClassUtils.isPresent(
|
||||
"org.codehaus.groovy.runtime.GStringImpl",
|
||||
RiakTemplate.class.getClassLoader());
|
||||
|
||||
protected Pattern bucketColonKey = Pattern.compile("(.+):(.+)");
|
||||
@@ -43,12 +44,13 @@ public class SimpleBucketKeyResolver implements BucketKeyResolver {
|
||||
Map m = (Map) o;
|
||||
Object bucket = m.get("bucket");
|
||||
Object key = m.get("key");
|
||||
bucketKeyPair = new SimpleBucketKeyPair<String, String>((null != bucket ? bucket.toString() : null),
|
||||
bucketKeyPair = new SimpleBucketKeyPair<String, String>((null != bucket ? bucket
|
||||
.toString() : null),
|
||||
(null != key ? key.toString() : null));
|
||||
} else if (o instanceof BucketKeyPair) {
|
||||
bucketKeyPair = (BucketKeyPair) o;
|
||||
} else if (groovyPresent && o instanceof GStringImpl) {
|
||||
bucketKeyPair = resolve(((GStringImpl) o).toString());
|
||||
bucketKeyPair = resolve(o.toString());
|
||||
}
|
||||
|
||||
return bucketKeyPair;
|
||||
|
||||
@@ -4,6 +4,10 @@ import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* An implementation of {@link org.springframework.datastore.riak.mapreduce.MapReduceOperation}
|
||||
* to represent an Erlang M/R function, which must be already defined inside the
|
||||
* Riak server.
|
||||
*
|
||||
* @author J. Brisbin <jon@jbrisbin.com>
|
||||
*/
|
||||
@SuppressWarnings({"unchecked"})
|
||||
@@ -20,10 +24,20 @@ public class ErlangMapReduceOperation implements MapReduceOperation {
|
||||
setFunction(function);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Erlang module this function is defined in.
|
||||
*
|
||||
* @param module
|
||||
*/
|
||||
public void setModule(String module) {
|
||||
moduleFunction.put("module", module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of this Erlang function.
|
||||
*
|
||||
* @param function
|
||||
*/
|
||||
public void setFunction(String function) {
|
||||
moduleFunction.put("function", function);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,9 @@ package org.springframework.datastore.riak.mapreduce;
|
||||
import org.springframework.datastore.riak.core.BucketKeyPair;
|
||||
|
||||
/**
|
||||
* An implementation of {@link org.springframework.datastore.riak.mapreduce.MapReduceOperation}
|
||||
* to describe a Javascript language M/R function.
|
||||
*
|
||||
* @author J. Brisbin <jon@jbrisbin.com>
|
||||
*/
|
||||
public class JavascriptMapReduceOperation implements MapReduceOperation {
|
||||
@@ -22,6 +25,11 @@ public class JavascriptMapReduceOperation implements MapReduceOperation {
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the anonymous source to use for the M/R function.
|
||||
*
|
||||
* @param source
|
||||
*/
|
||||
public void setSource(String source) {
|
||||
this.source = source;
|
||||
}
|
||||
@@ -30,6 +38,12 @@ public class JavascriptMapReduceOperation implements MapReduceOperation {
|
||||
return bucketKeyPair;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link org.springframework.datastore.riak.core.BucketKeyPair} to
|
||||
* point to for the Javascript to use in this M/R function.
|
||||
*
|
||||
* @param bucketKeyPair
|
||||
*/
|
||||
public void setBucketKeyPair(BucketKeyPair bucketKeyPair) {
|
||||
this.bucketKeyPair = bucketKeyPair;
|
||||
}
|
||||
|
||||
@@ -20,19 +20,56 @@ import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
/**
|
||||
* A generic interface to representing a Map/Reduce job to a data store that
|
||||
* supports that operation.
|
||||
*
|
||||
* @author J. Brisbin <jon@jbrisbin.com>
|
||||
*/
|
||||
public interface MapReduceJob<T> extends Callable {
|
||||
|
||||
List<Object> getInputs();
|
||||
/**
|
||||
* Get the list of inputs for this job.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
<V> List<V> getInputs();
|
||||
|
||||
/**
|
||||
* Set the list of inputs for this job.
|
||||
*
|
||||
* @param keys
|
||||
* @param <V>
|
||||
* @return
|
||||
*/
|
||||
<V> MapReduceJob addInputs(List<V> keys);
|
||||
|
||||
/**
|
||||
* Add a phase to this operation.
|
||||
*
|
||||
* @param phase
|
||||
* @return
|
||||
*/
|
||||
MapReduceJob addPhase(MapReducePhase phase);
|
||||
|
||||
/**
|
||||
* Set the static argument for this job.
|
||||
*
|
||||
* @param arg
|
||||
*/
|
||||
void setArg(T arg);
|
||||
|
||||
/**
|
||||
* Get the static argument for this job.
|
||||
*
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
<T> T getArg();
|
||||
|
||||
/**
|
||||
* Convert this job into the appropriate JSON to send to the server.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String toJson();
|
||||
}
|
||||
|
||||
@@ -17,10 +17,17 @@
|
||||
package org.springframework.datastore.riak.mapreduce;
|
||||
|
||||
/**
|
||||
* A generic interface to a Map/Reduce operation.
|
||||
*
|
||||
* @author J. Brisbin <jon@jbrisbin.com>
|
||||
*/
|
||||
public interface MapReduceOperation {
|
||||
|
||||
/**
|
||||
* Get the implementation-specific representation of a Map/Reduce operation.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Object getRepresentation();
|
||||
|
||||
}
|
||||
|
||||
@@ -20,14 +20,37 @@ import java.util.List;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* Generic interface to Map/Reduce in data stores that support it.
|
||||
*
|
||||
* @author J. Brisbin <jon@jbrisbin.com>
|
||||
*/
|
||||
public interface MapReduceOperations {
|
||||
|
||||
/**
|
||||
* Execute a {@link org.springframework.datastore.riak.mapreduce.MapReduceJob}
|
||||
* synchronously.
|
||||
*
|
||||
* @param job
|
||||
* @return
|
||||
*/
|
||||
Object execute(MapReduceJob job);
|
||||
|
||||
/**
|
||||
* Execute a MapReduceJob synchronously, converting the result into the given
|
||||
* type.
|
||||
*
|
||||
* @param job
|
||||
* @param targetType
|
||||
* @return The converted value.
|
||||
*/
|
||||
<T> T execute(MapReduceJob job, Class<T> targetType);
|
||||
|
||||
/**
|
||||
* Submit the job to run asynchronously.
|
||||
*
|
||||
* @param job
|
||||
* @return The Future representing the submitted job.
|
||||
*/
|
||||
<T> Future<List<T>> submit(MapReduceJob job);
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
package org.springframework.datastore.riak.mapreduce;
|
||||
|
||||
/**
|
||||
* A generic interface to the phases of Map/Reduce jobs.
|
||||
*
|
||||
* @author J. Brisbin <jon@jbrisbin.com>
|
||||
*/
|
||||
public interface MapReducePhase {
|
||||
@@ -27,10 +29,25 @@ public interface MapReducePhase {
|
||||
|
||||
Phase getPhase();
|
||||
|
||||
/**
|
||||
* The language this phase is described in.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getLanguage();
|
||||
|
||||
/**
|
||||
* Whether or not to keep the result of this phase.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean getKeepResults();
|
||||
|
||||
/**
|
||||
* Get the operation this phase will execute.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
MapReduceOperation getOperation();
|
||||
|
||||
}
|
||||
|
||||
@@ -31,6 +31,9 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* An implementation of {@link org.springframework.datastore.riak.mapreduce.MapReduceJob}
|
||||
* for the Riak data store.
|
||||
*
|
||||
* @author J. Brisbin <jon@jbrisbin.com>
|
||||
*/
|
||||
@SuppressWarnings({"unchecked"})
|
||||
@@ -54,7 +57,7 @@ public class RiakMapReduceJob implements MapReduceJob {
|
||||
this.riakTemplate = riakTemplate;
|
||||
}
|
||||
|
||||
public List<Object> getInputs() {
|
||||
public List getInputs() {
|
||||
return this.inputs;
|
||||
}
|
||||
|
||||
@@ -117,14 +120,17 @@ public class RiakMapReduceJob implements MapReduceJob {
|
||||
Object repr = phase.getOperation().getRepresentation();
|
||||
if (repr instanceof String) {
|
||||
// Using source
|
||||
json.writeStringField("source", String.format("%s", phase.getOperation().getRepresentation()));
|
||||
json.writeStringField("source",
|
||||
String.format("%s", phase.getOperation().getRepresentation()));
|
||||
} else if (repr instanceof BucketKeyPair) {
|
||||
BucketKeyPair pair = (BucketKeyPair) repr;
|
||||
json.writeStringField("bucket", String.format("%s", pair.getBucket()));
|
||||
json.writeStringField("bucket",
|
||||
String.format("%s", pair.getBucket()));
|
||||
json.writeStringField("key", String.format("%s", pair.getKey()));
|
||||
} else if (repr instanceof Map) {
|
||||
for (Map.Entry<Object, Object> entry : ((Map<Object, Object>) repr).entrySet()) {
|
||||
json.writeStringField(entry.getKey().toString(), entry.getValue().toString());
|
||||
json.writeStringField(entry.getKey().toString(),
|
||||
entry.getValue().toString());
|
||||
}
|
||||
}
|
||||
if (phase.getKeepResults()) {
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
package org.springframework.datastore.riak.mapreduce;
|
||||
|
||||
/**
|
||||
* An implementation of {@link org.springframework.datastore.riak.mapreduce.MapReducePhase}
|
||||
* for the Riak data store.
|
||||
*
|
||||
* @author J. Brisbin <jon@jbrisbin.com>
|
||||
*/
|
||||
public class RiakMapReducePhase implements MapReducePhase {
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010 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.datastore.riak.core;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration({"/org/springframework/datastore/RiakTemplateTests.xml"})
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public class RiakTemplateIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
ApplicationContext appCtx;
|
||||
@Autowired
|
||||
RiakTemplate riak;
|
||||
|
||||
public void testSet() {
|
||||
Map obj = new LinkedHashMap();
|
||||
obj.put("test", "value");
|
||||
obj.put("test2", 12);
|
||||
riak.set("test:test", obj);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetAsType() {
|
||||
TestObject obj = new TestObject();
|
||||
riak.set("test", obj);
|
||||
}
|
||||
|
||||
public void testSetInferringType() {
|
||||
Map obj = new LinkedHashMap();
|
||||
obj.put("test", "value");
|
||||
obj.put("test2", 12);
|
||||
riak.set("test", obj);
|
||||
}
|
||||
|
||||
public void testGetInferringType() {
|
||||
Map obj = riak.get("java.util.LinkedHashMap:test");
|
||||
assert null != obj;
|
||||
assert 12 == (Integer) obj.get("test2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAsType() {
|
||||
TestObject obj = riak.getAsType("test", TestObject.class);
|
||||
assert null != obj;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void conversions() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
|
||||
*
|
||||
* 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.datastore.riak.core
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.context.ApplicationContext
|
||||
import org.springframework.test.context.ContextConfiguration
|
||||
import spock.lang.Specification
|
||||
|
||||
/**
|
||||
* @author J. Brisbin <jon@jbrisbin.com>
|
||||
*/
|
||||
@ContextConfiguration(locations = "/org/springframework/datastore/RiakTemplateTests.xml")
|
||||
class RiakTemplateSpec extends Specification {
|
||||
|
||||
@Autowired
|
||||
ApplicationContext appCtx
|
||||
@Autowired
|
||||
RiakTemplate riak
|
||||
int run = 1
|
||||
|
||||
def "Test Map object with 'bucket:key' key"() {
|
||||
|
||||
given:
|
||||
def i = run++
|
||||
String val = "value $i"
|
||||
def objIn = [test: "value $i", integer: 12]
|
||||
riak.set("test:test", objIn)
|
||||
|
||||
when:
|
||||
def objOut = riak.get("test:test")
|
||||
|
||||
then:
|
||||
objOut.test == val
|
||||
|
||||
}
|
||||
|
||||
def "Test Map object with Map key"() {
|
||||
|
||||
given:
|
||||
def i = run++
|
||||
String val = "value $i"
|
||||
def objIn = [test: val, integer: 12]
|
||||
riak.set([bucket: "test", key: "test"], objIn)
|
||||
|
||||
when:
|
||||
def objOut = riak.get([bucket: "test", key: "test"])
|
||||
|
||||
then:
|
||||
objOut.test == val
|
||||
|
||||
}
|
||||
|
||||
def "Test custom object with 'bucket:key' key"() {
|
||||
|
||||
given:
|
||||
TestObject objIn = new TestObject()
|
||||
riak.set("test:test", objIn)
|
||||
|
||||
when:
|
||||
TestObject objOut = riak.get("test:test")
|
||||
|
||||
then:
|
||||
objOut.test == "value"
|
||||
|
||||
}
|
||||
|
||||
def "Test custom object with 'ClassName:key' key"() {
|
||||
|
||||
given:
|
||||
TestObject objIn = new TestObject()
|
||||
riak.set("test", objIn)
|
||||
|
||||
when:
|
||||
TestObject objOut = riak.getAsType("test", TestObject)
|
||||
|
||||
then:
|
||||
objOut.test == "value"
|
||||
|
||||
}
|
||||
|
||||
def "Test containsKey"() {
|
||||
|
||||
when:
|
||||
def containsKey = riak.containsKey("test:test")
|
||||
|
||||
then:
|
||||
true == containsKey
|
||||
|
||||
}
|
||||
|
||||
def "Test multiple get"() {
|
||||
|
||||
when:
|
||||
def objs = riak.getValues(["test:test", "${TestObject.name}:test"])
|
||||
|
||||
then:
|
||||
2 == objs.size()
|
||||
|
||||
}
|
||||
|
||||
def "Test getAndSet with Map"() {
|
||||
|
||||
given:
|
||||
def i = run++
|
||||
String val = "value $i"
|
||||
def newObj = [test: val, integer: 12]
|
||||
|
||||
when:
|
||||
def oldObj = riak.getAndSet("test:test", newObj)
|
||||
|
||||
then:
|
||||
"value" == oldObj.test
|
||||
|
||||
}
|
||||
|
||||
def "Test deleteKeys"() {
|
||||
|
||||
when:
|
||||
def deleted = riak.deleteKeys("test:test", "${TestObject.name}:test")
|
||||
|
||||
then:
|
||||
true == deleted
|
||||
|
||||
}
|
||||
|
||||
def "Test setMultipleIfKeysNonExistent with Map"() {
|
||||
|
||||
given:
|
||||
def newObj = [
|
||||
"test:test": [test: "value", integer: 12],
|
||||
"${TestObject.name}:test": [test: "value", integer: 12]
|
||||
]
|
||||
|
||||
when:
|
||||
def secondObj = riak.setMultipleIfKeysNonExistent(newObj).get("${TestObject.name}:test")
|
||||
secondObj.test = "newValue"
|
||||
def thirdObj = riak.setMultipleIfKeysNonExistent(["${TestObject.name}:test": secondObj]).get("${TestObject.name}:test")
|
||||
|
||||
then:
|
||||
"value" == thirdObj.test
|
||||
|
||||
cleanup:
|
||||
riak.deleteKeys("test:test", "${TestObject.name}:test")
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
|
||||
*
|
||||
* 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.datastore.riak.core;
|
||||
|
||||
/**
|
||||
* @author J. Brisbin <jon@jbrisbin.com>
|
||||
*/
|
||||
public class TestObject {
|
||||
String test = "value";
|
||||
Integer integer = 12;
|
||||
|
||||
public String getTest() {
|
||||
return test;
|
||||
}
|
||||
|
||||
public void setTest(String test) {
|
||||
this.test = test;
|
||||
}
|
||||
|
||||
public Integer getInteger() {
|
||||
return integer;
|
||||
}
|
||||
|
||||
public void setInteger(Integer integer) {
|
||||
this.integer = integer;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user