diff --git a/spring-data-riak/pom.xml b/spring-data-riak/pom.xml index 38f65f763..832cf13fd 100644 --- a/spring-data-riak/pom.xml +++ b/spring-data-riak/pom.xml @@ -127,7 +127,7 @@ com.springsource.bundlor.maven - org.spockframework spock-maven @@ -168,7 +168,7 @@ 2.7.7 - --> + diff --git a/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/AbstractRiakTemplate.java b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/AbstractRiakTemplate.java index abf45a9fd..3ed61af18 100644 --- a/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/AbstractRiakTemplate.java +++ b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/AbstractRiakTemplate.java @@ -168,6 +168,14 @@ public abstract class AbstractRiakTemplate extends RestGatewaySupport implements this.useCache = useCache; } + public QosParameters getDefaultQosParameters() { + return defaultQosParameters; + } + + public void setDefaultQosParameters(QosParameters defaultQosParameters) { + this.defaultQosParameters = defaultQosParameters; + } + /** * Extract the prefix from the URI for use in creating links. * diff --git a/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/RiakTemplate.java b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/RiakTemplate.java index ec7d1df40..a3abe7ba0 100644 --- a/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/RiakTemplate.java +++ b/spring-data-riak/src/main/java/org/springframework/data/keyvalue/riak/core/RiakTemplate.java @@ -527,6 +527,25 @@ public class RiakTemplate extends AbstractRiakTemplate implements BucketKeyValue */ @SuppressWarnings({"unchecked"}) public T linkWalk(B bucket, K key, String tag) { + return (T) linkWalkAsType(bucket, key, tag, null); + } + + /** + * Use Riak's link walking mechanism to retrieve a multipart message that will be decoded like + * they were individual objects (e.g. using the built-in HttpMessageConverters of + * RestTemplate) and return the result as a list of objects of one of:
  1. The type + * specified by requiredType
  2. If that's null, try using the bucket name + * in which the object was stored
  3. If all else fails, use a {@link java.util.Map}
  4. + *
+ * + * @param bucket + * @param key + * @param tag + * @param requiredType + * @return + */ + @SuppressWarnings({"unchecked"}) + public T linkWalkAsType(B bucket, K key, String tag, final Class requiredType) { final RestTemplate restTemplate = getRestTemplate(); final List types = new ArrayList(); types.add(MediaType.ALL); @@ -540,7 +559,7 @@ public class RiakTemplate extends AbstractRiakTemplate implements BucketKeyValue } }, new ResponseExtractor() { - @SuppressWarnings({"unchecked", "unchecked"}) + @SuppressWarnings({"unchecked"}) public Object extractData(ClientHttpResponse response) throws IOException { String contentType = ((List) response.getHeaders().get("Content-Type")).get(0) @@ -575,12 +594,17 @@ public class RiakTemplate extends AbstractRiakTemplate implements BucketKeyValue break; } } - Class clazz = Map.class; - if (null != bucketName) { + Class clazz = requiredType; + if (null == clazz && null != bucketName) { try { clazz = Class.forName(bucketName); } catch (ClassNotFoundException e) { + // Default to a Map. We know that will work. + clazz = Map.class; } + } else { + // Default to a Map. We know that will work. + clazz = Map.class; } // Can convert message? diff --git a/spring-data-riak/src/test/groovy/org/springframework/data/keyvalue/riak/core/RiakKeyValueTemplateSpec.groovy b/spring-data-riak/src/test/groovy/org/springframework/data/keyvalue/riak/core/RiakKeyValueTemplateSpec.groovy new file mode 100644 index 000000000..ae0d8cb58 --- /dev/null +++ b/spring-data-riak/src/test/groovy/org/springframework/data/keyvalue/riak/core/RiakKeyValueTemplateSpec.groovy @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2010 by J. Brisbin + * Portions (c) 2010 by NPC International, Inc. or the + * original author(s). + * + * 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.keyvalue.riak.core + +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.context.ApplicationContext +import org.springframework.data.keyvalue.riak.mapreduce.JavascriptMapReduceOperation +import org.springframework.data.keyvalue.riak.mapreduce.MapReduceJob +import org.springframework.data.keyvalue.riak.mapreduce.RiakMapReducePhase +import org.springframework.test.context.ContextConfiguration +import spock.lang.Shared +import spock.lang.Specification + +/** + * @author J. Brisbin + */ +@ContextConfiguration(locations = "/org/springframework/data/RiakKeyValueTemplateTests.xml") +class RiakKeyValueTemplateSpec extends Specification { + + @Autowired + ApplicationContext appCtx + @Autowired + RiakKeyValueTemplate riak + int run = 1 + @Shared def riakBin = System.properties["bamboo.RIAK_BIN"] ?: "/usr/sbin/riak" + @Shared def p + + def setupSpec() { + p = "$riakBin start".execute() + p.waitFor() + Thread.sleep(2000) + } + + def cleanupSpec() { + p = "$riakBin stop".execute() + p.waitFor() + } + + def "Test Map object"() { + + given: + def val = "value" + def objIn = [test: val, integer: 12] + riak.set("test:test", objIn) + + when: + def objOut = riak.get("test:test") + + then: + objOut.test == val + + } + + def "Test custom object"() { + + given: + TestObject objIn = new TestObject() + riak.set("${TestObject.name}:test", objIn) + + when: + TestObject objOut = riak.get("${TestObject.name}:test") + + then: + objOut.test == "value" + + } + + def "Test getting bucket schema"() { + + when: + def schema = riak.getBucketSchema("test", true) + + then: + "test" == schema.props.name + + } + + def "Test updating bucket schema"() { + + when: + def schema = riak.updateBucketSchema("test", [n_val: 2]).getBucketSchema("test") + + then: + 2 == schema.props.n_val + + } + + def "Test get with metadata"() { + + when: + def val = riak.getWithMetaData([bucket: "test", key: "test"], LinkedHashMap) + + then: + val.metaData.properties["Server"].contains("WebMachine") + + } + + def "Test setting QosParameters"() { + + given: + def obj = riak.get("test:test") + + when: + def qos = new RiakQosParameters() + qos.durableWriteThreshold = "all" + riak.set("test:test", obj, qos) + + then: + true + + } + + def "Test containsKey"() { + + when: + def containsKey = riak.containsKey([bucket: "test", key: "test"]) + + then: + true == containsKey + + } + + def "Test linking"() { + + given: + riak.link("${TestObject.name}:test", "test:test", "test") + + when: + def val = riak.getWithMetaData("test:test", Map) + def result = val.metaData.properties["Link"].find { it.contains("riaktag=\"test\"") } + + then: + null != result + + } + + def "Test link walking"() { + + when: + def val = riak.linkWalk("test:test", "test") + + then: + null != val + 1 == val.size() + val.get(0) instanceof TestObject + + } + + def "Test multiple get"() { + + when: + def objs = riak.getValues([ + new SimpleBucketKeyPair("test", "test"), + new SimpleBucketKeyPair(TestObject.name, "test") + ]) + + then: + 2 == objs.size() + + } + + def "Test getAndSet with Map"() { + + given: + def i = run++ + def newObj = [test: "value $i", integer: 12] + + when: + def oldObj = riak.getAndSet("test:test", newObj) + + then: + "value" == oldObj.test + + } + + def "Test setMultipleIfKeysNonExistent with Map"() { + + given: + def testKey = new SimpleBucketKeyPair("test", "test") + def testKey2 = new SimpleBucketKeyPair(TestObject.name, "test") + def newObj = [:] + newObj[testKey] = [test: "value", integer: 12] + newObj[testKey2] = [test: "value", integer: 12] + + when: + def secondObj = riak.setMultipleIfKeysNonExistent(newObj).get(testKey2) + secondObj.test = "newValue" + def updObj = [:] + updObj[testKey2] = secondObj + def thirdObj = riak.setMultipleIfKeysNonExistent(updObj).get(testKey2) + + then: + "value" == thirdObj.test + + } + + def "Test Map/Reduce returning Integer"() { + + given: + MapReduceJob job = riak.createMapReduceJob() + def mapJs = new JavascriptMapReduceOperation("function(v){ var o=Riak.mapValuesJson(v); return [1]; }") + def mapPhase = new RiakMapReducePhase("map", "javascript", mapJs) + + def reduceJs = new JavascriptMapReduceOperation("function(v){ var s=Riak.reduceSum(v); return s; }") + def reducePhase = new RiakMapReducePhase("reduce", "javascript", reduceJs) + + job.addInputs(["test"]). + addPhase(mapPhase). + addPhase(reducePhase) + println job.toJson() + + when: + def result = riak.execute(job, Integer) + + then: + 1 == result + + } + + def "Test Map/Reduce returning List"() { + + given: + MapReduceJob job = riak.createMapReduceJob() + def mapJs = new JavascriptMapReduceOperation("function(v){ ejsLog('/tmp/mapred.log', 'map v: '+JSON.stringify(v)); var o=Riak.mapValuesJson(v); return [1]; }") + def mapPhase = new RiakMapReducePhase("map", "javascript", mapJs) + + def reduceJs = new JavascriptMapReduceOperation("function(v){ ejsLog('/tmp/mapred.log', 'red v: '+JSON.stringify(v)); var s=Riak.reduceSum(v); return s; }") + def reducePhase = new RiakMapReducePhase("reduce", "javascript", reduceJs) + + job.addInputs(["test"]). + addPhase(mapPhase). + addPhase(reducePhase) + + when: + def result = riak.execute(job) + + then: + 1 == result.size() + 1 == result[0] + + } + + def "Test deleteKeys"() { + + given: + def testKey = new SimpleBucketKeyPair("test", "test") + def testKey2 = new SimpleBucketKeyPair(TestObject.name, "test") + + when: + def deleted = riak.deleteKeys(testKey, testKey2) + + then: + true == deleted + + } + +} \ No newline at end of file diff --git a/spring-data-riak/src/test/groovy/org/springframework/data/keyvalue/riak/core/RiakTemplateSpec.groovy b/spring-data-riak/src/test/groovy/org/springframework/data/keyvalue/riak/core/RiakTemplateSpec.groovy index 85898e401..82fd81625 100644 --- a/spring-data-riak/src/test/groovy/org/springframework/data/keyvalue/riak/core/RiakTemplateSpec.groovy +++ b/spring-data-riak/src/test/groovy/org/springframework/data/keyvalue/riak/core/RiakTemplateSpec.groovy @@ -1,11 +1,13 @@ /* * Copyright (c) 2010 by J. Brisbin + * Portions (c) 2010 by NPC International, Inc. or the + * original author(s). * * 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 + * 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, @@ -33,19 +35,19 @@ class RiakTemplateSpec extends Specification { @Autowired ApplicationContext appCtx @Autowired - RiakKeyValueTemplate riak + RiakTemplate riak int run = 1 - @Shared def riakBin = System.getenv("RIAK_BIN") + @Shared def riakBin = System.properties["bamboo.RIAK_BIN"] ?: "/usr/sbin/riak" @Shared def p def setupSpec() { - p = "/usr/sbin/riak start".execute() + p = "$riakBin start".execute() p.waitFor() Thread.sleep(2000) } def cleanupSpec() { - "/usr/sbin/riak stop".execute() + p = "$riakBin stop".execute() p.waitFor() } @@ -54,10 +56,10 @@ class RiakTemplateSpec extends Specification { given: def val = "value" def objIn = [test: val, integer: 12] - riak.set("test:test", objIn) + riak.set("test", "test", objIn) when: - def objOut = riak.get("test:test") + def objOut = riak.get("test", "test") then: objOut.test == val @@ -68,10 +70,10 @@ class RiakTemplateSpec extends Specification { given: TestObject objIn = new TestObject() - riak.set("${TestObject.name}:test", objIn) + riak.set(TestObject.name, "test", objIn) when: - TestObject objOut = riak.get("${TestObject.name}:test") + TestObject objOut = riak.get(TestObject.name, "test") then: objOut.test == "value" @@ -101,7 +103,7 @@ class RiakTemplateSpec extends Specification { def "Test get with metadata"() { when: - def val = riak.getWithMetaData([bucket: "test", key: "test"], LinkedHashMap) + def val = riak.getWithMetaData("test", "test", LinkedHashMap) then: val.metaData.properties["Server"].contains("WebMachine") @@ -111,12 +113,12 @@ class RiakTemplateSpec extends Specification { def "Test setting QosParameters"() { given: - def obj = riak.get("test:test") + def obj = riak.get("test", "test") when: def qos = new RiakQosParameters() qos.durableWriteThreshold = "all" - riak.set("test:test", obj, qos) + riak.set("test", "test", obj, qos) then: true @@ -126,7 +128,7 @@ class RiakTemplateSpec extends Specification { def "Test containsKey"() { when: - def containsKey = riak.containsKey([bucket: "test", key: "test"]) + def containsKey = riak.containsKey("test", "test") then: true == containsKey @@ -136,10 +138,10 @@ class RiakTemplateSpec extends Specification { def "Test linking"() { given: - riak.link("${TestObject.name}:test", "test:test", "test") + riak.link(TestObject.name, "test", "test", "test", "test") when: - def val = riak.getWithMetaData("test:test", Map) + def val = riak.getWithMetaData("test", "test", Map) def result = val.metaData.properties["Link"].find { it.contains("riaktag=\"test\"") } then: @@ -150,7 +152,7 @@ class RiakTemplateSpec extends Specification { def "Test link walking"() { when: - def val = riak.linkWalk("test:test", "test") + def val = riak.linkWalk("test", "test", "test") then: null != val @@ -159,16 +161,15 @@ class RiakTemplateSpec extends Specification { } - def "Test multiple get"() { + def "Test link walking as type"() { when: - def objs = riak.getValues([ - new SimpleBucketKeyPair("test", "test"), - new SimpleBucketKeyPair(TestObject.name, "test") - ]) + def val = riak.linkWalkAsType("test", "test", "test", Map) then: - 2 == objs.size() + null != val + 1 == val.size() + val.get(0) instanceof Map } @@ -179,42 +180,22 @@ class RiakTemplateSpec extends Specification { def newObj = [test: "value $i", integer: 12] when: - def oldObj = riak.getAndSet("test:test", newObj) + def oldObj = riak.getAndSet("test", "test", newObj) then: "value" == oldObj.test } - def "Test setMultipleIfKeysNonExistent with Map"() { - - given: - def testKey = new SimpleBucketKeyPair("test", "test") - def testKey2 = new SimpleBucketKeyPair(TestObject.name, "test") - def newObj = [:] - newObj[testKey] = [test: "value", integer: 12] - newObj[testKey2] = [test: "value", integer: 12] - - when: - def secondObj = riak.setMultipleIfKeysNonExistent(newObj).get(testKey2) - secondObj.test = "newValue" - def updObj = [:] - updObj[testKey2] = secondObj - def thirdObj = riak.setMultipleIfKeysNonExistent(updObj).get(testKey2) - - then: - "value" == thirdObj.test - - } - def "Test Map/Reduce returning Integer"() { given: MapReduceJob job = riak.createMapReduceJob() - def mapJs = new JavascriptMapReduceOperation("function(v){ var o=Riak.mapValuesJson(v); return [1]; }") + def uuid = UUID.randomUUID().toString() + def mapJs = new JavascriptMapReduceOperation("function(v){ var uuid='$uuid'; ejsLog('/tmp/mapred.log', 'map input: '+JSON.stringify(v)); var o=Riak.mapValuesJson(v); return [1]; }") def mapPhase = new RiakMapReducePhase("map", "javascript", mapJs) - def reduceJs = new JavascriptMapReduceOperation("function(v){ var s=Riak.reduceSum(v); return s; }") + def reduceJs = new JavascriptMapReduceOperation("function(v){ var uuid='$uuid'; ejsLog('/tmp/mapred.log', 'reduce input: '+JSON.stringify(v)); var s=Riak.reduceSum(v); ejsLog('/tmp/mapred.log', 'reduce output: '+JSON.stringify(s)); return s; }") def reducePhase = new RiakMapReducePhase("reduce", "javascript", reduceJs) job.addInputs(["test"]). @@ -234,10 +215,11 @@ class RiakTemplateSpec extends Specification { given: MapReduceJob job = riak.createMapReduceJob() - def mapJs = new JavascriptMapReduceOperation("function(v){ ejsLog('/tmp/mapred.log', 'map v: '+JSON.stringify(v)); var o=Riak.mapValuesJson(v); return [1]; }") + def uuid = UUID.randomUUID().toString() + def mapJs = new JavascriptMapReduceOperation("function(v){ var uuid='$uuid'; ejsLog('/tmp/mapred.log', 'map input: '+JSON.stringify(v)); var o=Riak.mapValuesJson(v); return [1]; }") def mapPhase = new RiakMapReducePhase("map", "javascript", mapJs) - def reduceJs = new JavascriptMapReduceOperation("function(v){ ejsLog('/tmp/mapred.log', 'red v: '+JSON.stringify(v)); var s=Riak.reduceSum(v); return s; }") + def reduceJs = new JavascriptMapReduceOperation("function(v){ var uuid='$uuid'; ejsLog('/tmp/mapred.log', 'reduce input: '+JSON.stringify(v)); var s=Riak.reduceSum(v); ejsLog('/tmp/mapred.log', 'reduce output: '+JSON.stringify(s)); return s; }") def reducePhase = new RiakMapReducePhase("reduce", "javascript", reduceJs) job.addInputs(["test"]). @@ -253,7 +235,7 @@ class RiakTemplateSpec extends Specification { } - def "Test deleteKeys"() { + def "Test delete key"() { given: def testKey = new SimpleBucketKeyPair("test", "test") diff --git a/spring-data-riak/src/test/resources/org/springframework/data/RiakKeyValueTemplateTests.xml b/spring-data-riak/src/test/resources/org/springframework/data/RiakKeyValueTemplateTests.xml new file mode 100644 index 000000000..43aafe1d5 --- /dev/null +++ b/spring-data-riak/src/test/resources/org/springframework/data/RiakKeyValueTemplateTests.xml @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/spring-data-riak/src/test/resources/org/springframework/data/RiakTemplateTests.xml b/spring-data-riak/src/test/resources/org/springframework/data/RiakTemplateTests.xml index 9238a146e..e8ebcfb28 100644 --- a/spring-data-riak/src/test/resources/org/springframework/data/RiakTemplateTests.xml +++ b/spring-data-riak/src/test/resources/org/springframework/data/RiakTemplateTests.xml @@ -1,11 +1,34 @@ + + + + class="org.springframework.data.keyvalue.riak.core.RiakTemplate" + p:defaultQosParameters-ref="qos"/>