+ renamed org.springframework.data.keyvalue.redis to o.s.d.redis

+ eliminated Riak package
+ eliminate Core package
This commit is contained in:
Costin Leau
2011-07-05 19:18:31 +03:00
parent f242a95620
commit 3217289401
293 changed files with 794 additions and 8579 deletions

10
.classpath Normal file
View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src/main/java"/>
<classpathentry kind="src" path="src/main/resources"/>
<classpathentry kind="src" path="src/test/java"/>
<classpathentry kind="src" path="src/test/resources"/>
<classpathentry exported="true" kind="con" path="com.springsource.sts.gradle.classpathcontainer"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="output" path="bin"/>
</classpath>

1
.gitignore vendored
View File

@@ -11,3 +11,4 @@ src/ant/.ant-targets-upload-dist.xml
*.iml
*.ipr
*.iws
*.log

34
.project Normal file
View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>spring-data-redis</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.springsource.sts.gradle.core.nature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
<filteredResources>
<filter>
<id>1309881117241</id>
<name></name>
<type>10</type>
<matcher>
<id>org.eclipse.ui.ide.orFilterMatcher</id>
<arguments>
<matcher>
<id>org.eclipse.ui.ide.multiFilter</id>
<arguments>1.0-projectRelativePath-equals-true-false-E:docs</arguments>
</matcher>
</arguments>
</matcher>
</filter>
</filteredResources>
</projectDescription>

View File

@@ -1,13 +1,12 @@
#
#Fri Jul 01 15:44:20 EEST 2011
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.compliance=1.6
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
#Tue Jul 05 18:51:57 EEST 2011
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.source=1.6
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.6
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.6

View File

@@ -1,9 +0,0 @@
#Mon Oct 11 17:02:20 EDT 2010
activeProfiles=
eclipse.preferences.version=1
fullBuildGoals=process-test-resources
includeModules=false
resolveWorkspaceProjects=true
resourceFilterGoals=process-resources resources\:testResources
skipCompilerPlugin=true
version=1

View File

@@ -45,9 +45,9 @@ For those in a hurry:
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="jedisFactory" class="org.springframework.data.keyvalue.redis.connection.jedis.JedisConnectionFactory"/>
<bean id="jedisFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"/>
<bean id="redisTemplate" class="org.springframework.data.keyvalue.redis.core.RedisTemplate"
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
p:connection-factory="jedisFactory"/>
</beans>

View File

@@ -1,6 +1,6 @@
// used for artifact names, building doc upload urls, etc.
description = 'Spring Data Key Value'
abbreviation = 'DATAKV'
description = 'Spring Data Redis'
abbreviation = 'DATAREDIS'
apply plugin: 'base'
apply plugin: 'idea'
@@ -30,7 +30,7 @@ buildscript {
}
allprojects {
group = 'org.springframework.data'
group = 'org.springframework.data.redis'
version = '1.0.0.BUILD-SNAPSHOT'
releaseBuild = version.endsWith('RELEASE')
@@ -48,49 +48,63 @@ allprojects {
mavenRepo name: "sonatype-snapshot", urls: "http://oss.sonatype.org/content/repositories/snapshots"
mavenRepo name: "jboss", urls: "http://repository.jboss.org/maven2/"
mavenRepo name: "java.net", urls: "http://download.java.net/maven/2/"
mavenRepo name: "ext-snapshots", urls: "http://springframework.svn.sourceforge.net/svnroot/springframework/repos/repo-ext/"
}
}
javaprojects = subprojects.findAll {
project -> project.path.startsWith(':spring-data-')
apply plugin: "java"
apply plugin: "maven"
apply plugin: 'eclipse' // `gradle eclipse` to generate .classpath/.project
apply plugin: 'idea' // `gradle idea` to generate .ipr/.iml
apply plugin: 'docbook'
apply plugin: 'bundlor' // all core projects should be OSGi-compliant
bundlor.useProjectProps = true
[compileJava, compileTestJava]*.options*.compilerArgs = ["-Xlint:-serial"]
// Common dependencies
dependencies {
// Logging
compile "org.slf4j:slf4j-api:$slf4jVersion"
compile "org.slf4j:jcl-over-slf4j:$slf4jVersion"
runtime "log4j:log4j:$log4jVersion"
runtime "org.slf4j:slf4j-log4j12:$slf4jVersion"
// Spring Framework
compile("org.springframework:spring-core:$springVersion") {
exclude module: "commons-logging"
}
compile "org.springframework:spring-beans:$springVersion"
compile "org.springframework:spring-context:$springVersion"
compile "org.springframework:spring-context-support:$springVersion"
compile "org.springframework:spring-tx:$springVersion"
compile("org.springframework:spring-oxm:$springVersion") { optional = true }
// Redis Drivers
compile "redis.clients:jedis:$jedisVersion"
compile("org.jredis:jredis-anthonylauzon:$jredisVersion") { optional = true }
compile("org.idevlab:rjc:$rjcVersion") { optional = true }
// Mappers
compile("org.codehaus.jackson:jackson-mapper-asl:$jacksonVersion") { optional = true }
compile("commons-beanutils:commons-beanutils-core:1.8.3") { optional = true }
// Testing
testCompile "junit:junit:$junitVersion"
testCompile "org.springframework:spring-test:$springVersion"
testCompile "org.mockito:mockito-all:$mockitoVersion"
testCompile("javax.annotation:jsr250-api:1.0") { optional = true }
testCompile("com.thoughtworks.xstream:xstream:1.3") { optional = true }
}
configure(javaprojects) {
apply plugin: "java"
apply plugin: "maven"
apply plugin: 'eclipse' // `gradle eclipse` to generate .classpath/.project
apply plugin: 'idea' // `gradle idea` to generate .ipr/.iml
apply plugin: 'docbook'
apply plugin: 'bundlor' // all core projects should be OSGi-compliant
javaprojects = rootProject
bundlor.useProjectProps = true
[compileJava, compileTestJava]*.options*.compilerArgs = ["-Xlint:-serial"]
// Common dependencies
dependencies {
// Logging
compile "org.slf4j:slf4j-api:$slf4jVersion"
compile "org.slf4j:jcl-over-slf4j:$slf4jVersion"
runtime "log4j:log4j:$log4jVersion"
runtime "org.slf4j:slf4j-log4j12:$slf4jVersion"
// Spring Framework
compile("org.springframework:spring-core:$springVersion") {
exclude module: "commons-logging"
}
compile "org.springframework:spring-beans:$springVersion"
compile "org.springframework:spring-context:$springVersion"
compile "org.springframework:spring-context-support:$springVersion"
compile "org.springframework:spring-tx:$springVersion"
// Jackson JSON Mapper
compile "org.codehaus.jackson:jackson-mapper-asl:$jacksonVersion"
// Testing
testCompile "junit:junit:$junitVersion"
testCompile "org.springframework:spring-test:$springVersion"
testCompile "org.mockito:mockito-all:$mockitoVersion"
}
apply from: "$rootDir/maven.gradle"
}
sourceCompatibility = 1.6
targetCompatibility = 1.6
apply from: "$rootDir/maven.gradle"
ideaProject {
withXml { provider ->
@@ -122,9 +136,6 @@ task dist(type: Zip) {
include 'reference/**/*'
include 'api/**/*'
}
into('dist') {
from javaprojects.collect {project -> project.libsDir }
}
}
doLast {
ant.checksum(file: archivePath, algorithm: 'SHA1', fileext: '.sha1')
@@ -137,4 +148,6 @@ task uploadDist(type: org.springframework.gradle.tasks.S3DistroUpload, dependsOn
archiveFile = dist.archivePath
projectKey = 'DATAKV'
projectName = 'Spring Data Key Value'
}
}
defaultTasks 'clean', 'build'

6
docs/.classpath Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry exported="true" kind="con" path="com.springsource.sts.gradle.classpathcontainer"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>spring-data-core</name>
<name>spring-data-redis.docs</name>
<comment></comment>
<projects>
</projects>
@@ -12,6 +12,7 @@
</buildCommand>
</buildSpec>
<natures>
<nature>com.springsource.sts.gradle.core.nature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View File

@@ -1,4 +1,3 @@
import org.apache.tools.ant.filters.FixCrLfFilter
import org.apache.tools.ant.filters.ReplaceTokens
// -----------------------------------------------------------------------------
@@ -10,7 +9,6 @@ apply plugin: 'docbook'
assemble.dependsOn = ['api', 'docbook']
[docbookHtml, docbookFoPdf, docbookHtmlSingle]*.group = 'Documentation'
[docbookHtml, docbookFoPdf, docbookHtmlSingle]*.sourceFileName = 'index.xml'
[docbookHtml, docbookFoPdf, docbookHtmlSingle]*.sourceDirectory = new File(projectDir, 'src/reference/docbook')
@@ -46,7 +44,6 @@ task reference (type: Copy) {
dependsOn 'docbook'
description = "Builds aggregated DocBook"
group = "Documentation"
logger.info("Version is " + version)
destinationDir = buildDir
with(refSpec)
}
@@ -71,9 +68,7 @@ task api(type: Javadoc) {
breakIterator = true
showFromProtected()
groups = [
'Spring Data Key Value Core': ['org.springframework.data.keyvalue*'],
'Spring Data Redis Support' : ['org.springframework.data.keyvalue.redis*'],
'Spring Data Riak Support' : ['org.springframework.data.keyvalue.riak*']
'Spring Data Redis Support' : ['org.springframework.data.redis*'],
]
links = [

View File

@@ -5,9 +5,9 @@
-quiet
-docfilessubdirs
-group "Spring Data Key Value Core" "org.springframework.data.keyvalue*"
-group "Spring Data Redis Support" "org.springframework.data.keyvalue.redis*"
-group "Spring Data Redis Support" "org.springframework.data.redis*"
-group "Spring Data Riak Support" "org.springframework.data.keyvalue.riak*"
-link http://static.springframework.org/spring/docs/3.0.x/javadoc-api
-link http://download.oracle.com/javase/6/docs/api/
-exclude org.springframework.data.keyvalue.redis.config
-exclude org.springframework.data.redis.config

View File

@@ -15,8 +15,8 @@
listener containers that is used to create Message-Driven POJOs
(MDPs) and for synchronous reception, the <interfacename>RedisConnection</interfacename> contract.</para>
<para>The package <literal>org.springframework.data.keyvalue.redis.connection</literal> and
<literal>org.springframework.data.keyvalue.redis.listener</literal> provide
<para>The package <literal>org.springframework.data.redis.connection</literal> and
<literal>org.springframework.data.redis.listener</literal> provide
the core functionality for using Redis messaging.</para>
<section id="redis:pubsub:publish">
@@ -168,20 +168,20 @@ template.convertAndSend("hello!", "world");]]></programlisting>
is displayed below:</para>
<programlisting language="xml"><lineannotation>&lt;!-- this is the Message Driven POJO (MDP) --&gt;</lineannotation>
<emphasis role="bold">&lt;bean id="messageListener" class="org.springframework.data.keyvalue.redis.listener.adapter.MessageListenerAdapter"&gt;</emphasis>
<emphasis role="bold">&lt;bean id="messageListener" class="org.springframework.data.redis.listener.adapter.MessageListenerAdapter"&gt;</emphasis>
&lt;constructor-arg&gt;
&lt;bean class="redisexample.DefaultMessageDelegate"/&gt;
&lt;/constructor-arg&gt;
&lt;/bean&gt;
<lineannotation>&lt;!-- and this is the message listener container... --&gt;</lineannotation>
&lt;bean id="redisContainer" class="org.springframework.data.keyvalue.redis.listener.RedisMessageListenerContainer"&gt;
&lt;bean id="redisContainer" class="org.springframework.data.redis.listener.RedisMessageListenerContainer"&gt;
&lt;property name="connectionFactory" ref="connectionFactory"/&gt;
&lt;property name="messageListeners"&gt;
<lineannotation>&lt;!-- map of listeners and their associated topics (channels or/and patterns) --&gt;</lineannotation>
&lt;map&gt;
<emphasis role="bold">&lt;entry key-ref="messageListener"&gt;</emphasis>
&lt;bean class="org.springframework.data.keyvalue.redis.listener.ChannelTopic">
&lt;bean class="org.springframework.data.redis.listener.ChannelTopic">
&lt;constructor-arg value="chatroom"&gt;
&lt;/bean&gt;
&lt;/entry&gt;

View File

@@ -46,7 +46,7 @@
<para>One of the first tasks when using Redis and Spring is to connect to the store through the IoC container. To do that, a Java connector (or binding) is required;
currently SDKV has support for Jedis and JRedis. No matter the library one chooses, there only one set of SDKV API that one needs to use that behaves consistently
across all connectors, namely the <literal>org.springframework.data.keyvalue.redis.connection</literal> package and its
across all connectors, namely the <literal>org.springframework.data.redis.connection</literal> package and its
<interfacename>RedisConnection</interfacename> and <interfacename>RedisConnectionFactory</interfacename> interfaces for working respectively for retrieving active
<literal>connection</literal> to Redis.</para>
@@ -86,7 +86,7 @@
<title>Configuring Jedis connector</title>
<para><ulink url="http://github.com/xetorthio/jedis">Jedis</ulink> is one of the connectors supported by the Key Value module through the
<literal>org.springframework.data.keyvalue.redis.connection.jedis</literal> package. In its simples form, the Jedis configuration looks as follow:</para>
<literal>org.springframework.data.redis.connection.jedis</literal> package. In its simples form, the Jedis configuration looks as follow:</para>
<programlisting language="xml"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
@@ -95,7 +95,7 @@
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- Jedis ConnectionFactory -->
<bean id="jedisConnectionFactory" class="org.springframework.data.keyvalue.redis.connection.jedis.JedisConnectionFactory"/>
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"/>
</beans>]]></programlisting>
<para>For production use however, one might want to tweak the settings such as the host or password:</para>
@@ -107,7 +107,7 @@
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="jedisConnectionFactory" class="org.springframework.data.keyvalue.redis.connection.jedis.JedisConnectionFactory"
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
p:host-name="server" p:port="6379"/>
</beans>]]></programlisting>
@@ -117,7 +117,7 @@
<title>Configuring JRedis connector</title>
<para><ulink url="http://github.com/alphazero/jredis">JRedis</ulink> is another popular, open-source connector supported by SDKV through the
<literal>org.springframework.data.keyvalue.redis.connection.jredis</literal> package.</para>
<literal>org.springframework.data.redis.connection.jredis</literal> package.</para>
<note>Since JRedis itself does not support (yet) Redis 2.x commands, SDKV uses an updated fork available
<ulink url="http://github.com/anthonylauzon/jredis">here</ulink>.</note>
@@ -130,7 +130,7 @@
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="jredisConnectionFactory" class="org.springframework.data.keyvalue.redis.connection.jredis.JredisConnectionFactory"
<bean id="jredisConnectionFactory" class="org.springframework.data.redis.connection.jredis.JredisConnectionFactory"
p:host-name="server" p:port="6379"/>
</beans>]]></programlisting>
@@ -149,7 +149,7 @@
<title>Configuring RJC connector</title>
<para><ulink url="http://github.com/e-mzungu/rjc">RJC</ulink> is the third, open-source connector supported by SDKV through the
<literal>org.springframework.data.keyvalue.redis.connection.rjc</literal> package.</para>
<literal>org.springframework.data.redis.connection.rjc</literal> package.</para>
<para>Similar to the other connectors, a typical RJC configuration can looks like this:</para>
@@ -160,7 +160,7 @@
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="jredisConnectionFactory" class="org.springframework.data.keyvalue.redis.connection.rjc.RjcConnectionFactory"
<bean id="jredisConnectionFactory" class="org.springframework.data.redis.connection.rjc.RjcConnectionFactory"
p:host-name="server" p:port="6379"/>
</beans>]]></programlisting>
@@ -180,7 +180,7 @@
<section id="redis:template">
<title>Working with Objects through <classname>RedisTemplate</classname></title>
<para>Most users are likely to use <classname>RedisTemplate</classname> and its coresponding package <literal>org.springframework.data.keyvalue.redis.core</literal> - the
<para>Most users are likely to use <classname>RedisTemplate</classname> and its coresponding package <literal>org.springframework.data.redis.core</literal> - the
template is in fact the central class of the Redis module due to its rich feature set.
The template offers a high-level abstraction for Redis interaction - while <interfacename>RedisConnection</interfacename> offer low level methods that accept and return
binary values (<literal>byte</literal> arrays), the template takes care of serialization and connection management, freeing the user from dealing with such details.</para>
@@ -256,7 +256,7 @@
<para>Out of the box, <classname>RedisTemplate</classname> uses a Java-based serializer for most of its operations. This means that any object written or read by the template will be
serializer/deserialized through Java. The serialization mechanism can be easily changed on the template and the Redis module offers several implementations available in the
<literal>org.springframework.data.keyvalue.redis.serializer</literal> package - see <xref linkend="redis:serializer"/> for more information.
<literal>org.springframework.data.redis.serializer</literal> package - see <xref linkend="redis:serializer"/> for more information.
Note that the template requires all keys to be non-null - values can be null as long as the underlying
serializer accepts them; read the javadoc of each serializer for more information.</para>
@@ -270,11 +270,11 @@
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="jedisConnectionFactory" class="org.springframework.data.keyvalue.redis.connection.jedis.JedisConnectionFactory"
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
p:use-pool="true"/>
<!-- redis template definition -->
<bean id="redisTemplate" class="org.springframework.data.keyvalue.redis.core.RedisTemplate"
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
p:connection-factory-ref="jedisConnectionFactory"/>
...
@@ -314,10 +314,10 @@
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="jedisConnectionFactory" class="org.springframework.data.keyvalue.redis.connection.jedis.JedisConnectionFactory"
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
p:use-pool="true"/>
<bean id="stringRedisTemplate" class="org.springframework.data.keyvalue.redis.core.StringRedisTemplate"
<bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate"
p:connection-factory-ref="jedisConnectionFactory"/>
...
@@ -355,7 +355,7 @@
<para>From the framework perspective, the data stored in Redis are just bytes. While Redis itself supports various types, for the most part these refer to the way the data is stored
rather then what it represents. It is up to the user to decide whether the information gets translated into Strings or any other objects. The conversion between the user (custom)
types and raw data (and vice-versa) is handled in SDKV Redis through the <interfacename>RedisSerializer</interfacename> interface
(package <literal>org.springframework.data.keyvalue.redis.serializer</literal>) which as the name implies, takes care of the serialization process. Multiple implementations are
(package <literal>org.springframework.data.redis.serializer</literal>) which as the name implies, takes care of the serialization process. Multiple implementations are
available out of the box, two of which have been already mentioned before in this documentation: the <literal>StringRedisSerializer</literal> and
the <literal>JdkSerializationRedisSerializer</literal>. However one can use <classname>OxmSerializer</classname> for Object/XML mapping through Spring 3
<ulink url="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/oxm.html">OXM</ulink> support or <classname>JacksonJsonRedisSerializer</classname> for storing
@@ -368,7 +368,7 @@
<section id="redis:support">
<title>Support Classes</title>
<para>Package <literal>org.springframework.data.keyvalue.redis.support</literal> offers various reusable components that rely on Redis as a backing store. Curently the package contains
<para>Package <literal>org.springframework.data.redis.support</literal> offers various reusable components that rely on Redis as a backing store. Curently the package contains
various JDK-based interface implementations on top of Redis such as <ulink url="http://download.oracle.com/javase/6/docs/api/java/util/concurrent/atomic/package-summary.html">atomic</ulink>
counters and JDK <interfacename><ulink url="http://download.oracle.com/javase/6/docs/api/java/util/Collection.html">Collections</ulink></interfacename>.</para>
@@ -385,7 +385,7 @@
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="queue" class="org.springframework.data.keyvalue.redis.support.collections.DefaultRedisList">
<bean id="queue" class="org.springframework.data.redis.support.collections.DefaultRedisList">
<constructor-arg ref="redisTemplat"/>
<constructor-arg value="queue-key"/>
</bean>

View File

@@ -12,9 +12,22 @@ jacksonVersion = 1.6.4
junitVersion = 4.8.1
mockitoVersion = 1.8.5
# Drivers
jedisVersion = 2.0.0
jredisVersion = 03122010
rjcVersion= 0.6.4
# Manifest properties
## OSGi ranges
spring.range = "[3.0.0, 4.0.0)"
jedis.range = "[2.0.0, 2.0.0]"
jackson.range = "[1.6, 2.0.0)"
rjc.range = "[0.6.4, 0.6.4]"
# --------------------
# Project wide version
# --------------------
springDataKeyValueVersion=1.0.0.BUILD-SNAPSHOT
version = 'springDataKeyValueVersion'
version = 'springDataKeyValueVersion'

View File

@@ -1,8 +1,5 @@
rootProject.name = 'spring-data-key-value'
rootProject.name = 'spring-data-redis'
include 'docs'
include "spring-data-keyvalue-core",
"spring-data-redis",
"spring-data-riak"
docs = findProject(':docs')

View File

@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="output" path="bin"/>
<classpathentry kind="src" path="src/main/java"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER" exported="true"/>
<classpathentry sourcepath="C:/Users/costin/.gradle/cache/org.codehaus.jackson/jackson-mapper-asl/sources/jackson-mapper-asl-1.6.4-sources.jar" kind="lib" path="C:/Users/costin/.gradle/cache/org.codehaus.jackson/jackson-mapper-asl/jars/jackson-mapper-asl-1.6.4.jar" exported="true"/>
<classpathentry sourcepath="C:/Users/costin/.gradle/cache/org.slf4j/slf4j-log4j12/sources/slf4j-log4j12-1.6.1-sources.jar" kind="lib" path="C:/Users/costin/.gradle/cache/org.slf4j/slf4j-log4j12/jars/slf4j-log4j12-1.6.1.jar" exported="true"/>
<classpathentry sourcepath="C:/Users/costin/.gradle/cache/org.springframework/spring-tx/sources/spring-tx-3.0.5.RELEASE-sources.jar" kind="lib" path="C:/Users/costin/.gradle/cache/org.springframework/spring-tx/jars/spring-tx-3.0.5.RELEASE.jar" exported="true"/>
<classpathentry sourcepath="C:/Users/costin/.gradle/cache/org.springframework/spring-context-support/sources/spring-context-support-3.0.5.RELEASE-sources.jar" kind="lib" path="C:/Users/costin/.gradle/cache/org.springframework/spring-context-support/jars/spring-context-support-3.0.5.RELEASE.jar" exported="true"/>
<classpathentry sourcepath="C:/Users/costin/.gradle/cache/org.springframework/spring-test/sources/spring-test-3.0.5.RELEASE-sources.jar" kind="lib" path="C:/Users/costin/.gradle/cache/org.springframework/spring-test/jars/spring-test-3.0.5.RELEASE.jar" exported="true"/>
<classpathentry sourcepath="C:/Users/costin/.gradle/cache/org.springframework/spring-aop/sources/spring-aop-3.0.5.RELEASE-sources.jar" kind="lib" path="C:/Users/costin/.gradle/cache/org.springframework/spring-aop/jars/spring-aop-3.0.5.RELEASE.jar" exported="true"/>
<classpathentry sourcepath="C:/Users/costin/.gradle/cache/org.springframework/spring-expression/sources/spring-expression-3.0.5.RELEASE-sources.jar" kind="lib" path="C:/Users/costin/.gradle/cache/org.springframework/spring-expression/jars/spring-expression-3.0.5.RELEASE.jar" exported="true"/>
<classpathentry sourcepath="C:/Users/costin/.gradle/cache/log4j/log4j/sources/log4j-1.2.16-sources.jar" kind="lib" path="C:/Users/costin/.gradle/cache/log4j/log4j/bundles/log4j-1.2.16.jar" exported="true"/>
<classpathentry sourcepath="C:/Users/costin/.gradle/cache/org.codehaus.jackson/jackson-core-asl/sources/jackson-core-asl-1.6.4-sources.jar" kind="lib" path="C:/Users/costin/.gradle/cache/org.codehaus.jackson/jackson-core-asl/jars/jackson-core-asl-1.6.4.jar" exported="true"/>
<classpathentry sourcepath="C:/Users/costin/.gradle/cache/aopalliance/aopalliance/sources/aopalliance-1.0-sources.jar" kind="lib" path="C:/Users/costin/.gradle/cache/aopalliance/aopalliance/jars/aopalliance-1.0.jar" exported="true"/>
<classpathentry sourcepath="C:/Users/costin/.gradle/cache/org.slf4j/slf4j-api/sources/slf4j-api-1.6.1-sources.jar" kind="lib" path="C:/Users/costin/.gradle/cache/org.slf4j/slf4j-api/jars/slf4j-api-1.6.1.jar" exported="true"/>
<classpathentry sourcepath="C:/Users/costin/.gradle/cache/org.springframework/spring-asm/sources/spring-asm-3.0.5.RELEASE-sources.jar" kind="lib" path="C:/Users/costin/.gradle/cache/org.springframework/spring-asm/jars/spring-asm-3.0.5.RELEASE.jar" exported="true"/>
<classpathentry sourcepath="C:/Users/costin/.gradle/cache/org.mockito/mockito-all/sources/mockito-all-1.8.5-sources.jar" kind="lib" path="C:/Users/costin/.gradle/cache/org.mockito/mockito-all/jars/mockito-all-1.8.5.jar" exported="true"/>
<classpathentry sourcepath="C:/Users/costin/.gradle/cache/junit/junit/sources/junit-4.8.1-sources.jar" kind="lib" path="C:/Users/costin/.gradle/cache/junit/junit/jars/junit-4.8.1.jar" exported="true"/>
<classpathentry sourcepath="C:/Users/costin/.gradle/cache/org.slf4j/jcl-over-slf4j/sources/jcl-over-slf4j-1.6.1-sources.jar" kind="lib" path="C:/Users/costin/.gradle/cache/org.slf4j/jcl-over-slf4j/jars/jcl-over-slf4j-1.6.1.jar" exported="true"/>
<classpathentry sourcepath="C:/Users/costin/.gradle/cache/org.springframework/spring-core/sources/spring-core-3.0.5.RELEASE-sources.jar" kind="lib" path="C:/Users/costin/.gradle/cache/org.springframework/spring-core/jars/spring-core-3.0.5.RELEASE.jar" exported="true"/>
<classpathentry sourcepath="C:/Users/costin/.gradle/cache/org.springframework/spring-context/sources/spring-context-3.0.5.RELEASE-sources.jar" kind="lib" path="C:/Users/costin/.gradle/cache/org.springframework/spring-context/jars/spring-context-3.0.5.RELEASE.jar" exported="true"/>
<classpathentry sourcepath="C:/Users/costin/.gradle/cache/org.springframework/spring-beans/sources/spring-beans-3.0.5.RELEASE-sources.jar" kind="lib" path="C:/Users/costin/.gradle/cache/org.springframework/spring-beans/jars/spring-beans-3.0.5.RELEASE.jar" exported="true"/>
</classpath>

View File

@@ -1,13 +0,0 @@
#
#Thu Apr 21 21:26:44 EEST 2011
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.compliance=1.5
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.source=1.5
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error

View File

@@ -1,27 +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.data.keyvalue;
import org.springframework.dao.UncategorizedDataAccessException;
public class UncategorizedKeyvalueStoreException extends UncategorizedDataAccessException {
public UncategorizedKeyvalueStoreException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -1,19 +0,0 @@
Bundle-SymbolicName: org.springframework.data.keyvalue
Bundle-Name: Spring data Key-Value
Bundle-Vendor: SpringSource
Bundle-ManifestVersion: 2
Import-Package:
sun.reflect;version="0";resolution:=optional
Import-Template:
org.springframework.beans.*;version="[3.0.0, 4.0.0)",
org.springframework.core.*;version="[3.0.0, 4.0.0)",
org.springframework.dao.*;version="[3.0.0, 4.0.0)",
org.springframework.util.*;version="[3.0.0, 4.0.0)",
org.springframework.data.core.*;version="[1.0.0, 2.0.0)",
org.springframework.data.core.*;version="[1.0.0, 2.0.0)",
org.springframework.data.persistence.*;version="[1.0.0, 2.0.0)",
org.aopalliance.*;version="[1.0.0, 2.0.0)";resolution:=optional,
org.apache.commons.logging.*;version="[1.1.1, 2.0.0)",
org.w3c.dom.*;version="0"

View File

@@ -1,38 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src/main/java"/>
<classpathentry kind="src" path="src/main/resources"/>
<classpathentry kind="src" path="src/test/java"/>
<classpathentry kind="src" path="src/test/resources"/>
<classpathentry exported="true" kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry exported="true" kind="lib" path="C:/Users/costin/.gradle/cache/org.slf4j/slf4j-log4j12/jars/slf4j-log4j12-1.6.1.jar" sourcepath="C:/Users/costin/.gradle/cache/org.slf4j/slf4j-log4j12/sources/slf4j-log4j12-1.6.1-sources.jar"/>
<classpathentry exported="true" kind="lib" path="C:/Users/costin/.gradle/cache/org.springframework/spring-context-support/jars/spring-context-support-3.0.5.RELEASE.jar" sourcepath="C:/Users/costin/.gradle/cache/org.springframework/spring-context-support/sources/spring-context-support-3.0.5.RELEASE-sources.jar"/>
<classpathentry exported="true" kind="lib" path="C:/Users/costin/.gradle/cache/org.springframework/spring-expression/jars/spring-expression-3.0.5.RELEASE.jar" sourcepath="C:/Users/costin/.gradle/cache/org.springframework/spring-expression/sources/spring-expression-3.0.5.RELEASE-sources.jar"/>
<classpathentry exported="true" kind="lib" path="C:/Users/costin/.gradle/cache/org.springframework/spring-oxm/jars/spring-oxm-3.0.5.RELEASE.jar" sourcepath="C:/Users/costin/.gradle/cache/org.springframework/spring-oxm/sources/spring-oxm-3.0.5.RELEASE-sources.jar"/>
<classpathentry exported="true" kind="lib" path="C:/Users/costin/.gradle/cache/log4j/log4j/bundles/log4j-1.2.16.jar" sourcepath="C:/Users/costin/.gradle/cache/log4j/log4j/sources/log4j-1.2.16-sources.jar"/>
<classpathentry exported="true" kind="lib" path="C:/Users/costin/.gradle/cache/org.idevlab/rjc/bundles/rjc-0.6.4.jar" sourcepath="C:/Users/costin/.gradle/cache/org.idevlab/rjc/sources/rjc-0.6.4-sources.jar"/>
<classpathentry exported="true" kind="lib" path="C:/Users/costin/.gradle/cache/org.jredis/jredis-anthonylauzon/jars/jredis-anthonylauzon-03122010.jar" sourcepath="C:/Users/costin/.gradle/cache/org.jredis/jredis-anthonylauzon/sources/jredis-anthonylauzon-03122010-sources.jar"/>
<classpathentry exported="true" kind="lib" path="C:/Users/costin/.gradle/cache/org.codehaus.jackson/jackson-core-asl/jars/jackson-core-asl-1.6.4.jar" sourcepath="C:/Users/costin/.gradle/cache/org.codehaus.jackson/jackson-core-asl/sources/jackson-core-asl-1.6.4-sources.jar"/>
<classpathentry exported="true" kind="lib" path="C:/Users/costin/.gradle/cache/aopalliance/aopalliance/jars/aopalliance-1.0.jar" sourcepath="C:/Users/costin/.gradle/cache/aopalliance/aopalliance/sources/aopalliance-1.0-sources.jar"/>
<classpathentry exported="true" kind="lib" path="C:/Users/costin/.gradle/cache/org.slf4j/slf4j-api/jars/slf4j-api-1.6.1.jar" sourcepath="C:/Users/costin/.gradle/cache/org.slf4j/slf4j-api/sources/slf4j-api-1.6.1-sources.jar"/>
<classpathentry exported="true" kind="lib" path="C:/Users/costin/.gradle/cache/org.springframework/spring-asm/jars/spring-asm-3.0.5.RELEASE.jar" sourcepath="C:/Users/costin/.gradle/cache/org.springframework/spring-asm/sources/spring-asm-3.0.5.RELEASE-sources.jar"/>
<classpathentry exported="true" kind="lib" path="C:/Users/costin/.gradle/cache/xpp3/xpp3_min/jars/xpp3_min-1.1.4c.jar" sourcepath="C:/Users/costin/.gradle/cache/xpp3/xpp3_min/sources/xpp3_min-1.1.4c-sources.jar"/>
<classpathentry exported="true" kind="lib" path="C:/Users/costin/.gradle/cache/org.springframework/spring-core/jars/spring-core-3.0.5.RELEASE.jar" sourcepath="C:/Users/costin/.gradle/cache/org.springframework/spring-core/sources/spring-core-3.0.5.RELEASE-sources.jar"/>
<classpathentry exported="true" kind="lib" path="C:/Users/costin/.gradle/cache/org.springframework/spring-context/jars/spring-context-3.0.5.RELEASE.jar" sourcepath="C:/Users/costin/.gradle/cache/org.springframework/spring-context/sources/spring-context-3.0.5.RELEASE-sources.jar"/>
<classpathentry exported="true" kind="lib" path="C:/Users/costin/.gradle/cache/org.springframework/spring-beans/jars/spring-beans-3.0.5.RELEASE.jar" sourcepath="C:/Users/costin/.gradle/cache/org.springframework/spring-beans/sources/spring-beans-3.0.5.RELEASE-sources.jar"/>
<classpathentry exported="true" kind="lib" path="C:/Users/costin/.gradle/cache/org.codehaus.jackson/jackson-mapper-asl/jars/jackson-mapper-asl-1.6.4.jar" sourcepath="C:/Users/costin/.gradle/cache/org.codehaus.jackson/jackson-mapper-asl/sources/jackson-mapper-asl-1.6.4-sources.jar"/>
<classpathentry exported="true" kind="lib" path="C:/Users/costin/.gradle/cache/org.springframework/spring-tx/jars/spring-tx-3.0.5.RELEASE.jar" sourcepath="C:/Users/costin/.gradle/cache/org.springframework/spring-tx/sources/spring-tx-3.0.5.RELEASE-sources.jar"/>
<classpathentry exported="true" kind="lib" path="C:/Users/costin/.gradle/cache/org.springframework/spring-test/jars/spring-test-3.0.5.RELEASE.jar" sourcepath="C:/Users/costin/.gradle/cache/org.springframework/spring-test/sources/spring-test-3.0.5.RELEASE-sources.jar"/>
<classpathentry exported="true" kind="lib" path="C:/Users/costin/.gradle/cache/org.springframework/spring-aop/jars/spring-aop-3.0.5.RELEASE.jar" sourcepath="C:/Users/costin/.gradle/cache/org.springframework/spring-aop/sources/spring-aop-3.0.5.RELEASE-sources.jar"/>
<classpathentry exported="true" kind="lib" path="C:/Users/costin/.gradle/cache/redis.clients/jedis/jars/jedis-2.0.0.jar" sourcepath="C:/Users/costin/.gradle/cache/redis.clients/jedis/sources/jedis-2.0.0-sources.jar"/>
<classpathentry exported="true" kind="lib" path="C:/Users/costin/.gradle/cache/commons-pool/commons-pool/jars/commons-pool-1.5.6.jar" sourcepath="C:/Users/costin/.gradle/cache/commons-pool/commons-pool/sources/commons-pool-1.5.6-sources.jar"/>
<classpathentry exported="true" kind="lib" path="C:/Users/costin/.gradle/cache/javax.annotation/jsr250-api/jars/jsr250-api-1.0.jar" sourcepath="C:/Users/costin/.gradle/cache/javax.annotation/jsr250-api/sources/jsr250-api-1.0-sources.jar"/>
<classpathentry exported="true" kind="lib" path="C:/Users/costin/.gradle/cache/commons-beanutils/commons-beanutils-core/jars/commons-beanutils-core-1.8.3.jar"/>
<classpathentry exported="true" kind="lib" path="C:/Users/costin/.gradle/cache/com.thoughtworks.xstream/xstream/jars/xstream-1.3.jar" sourcepath="C:/Users/costin/.gradle/cache/com.thoughtworks.xstream/xstream/sources/xstream-1.3-sources.jar"/>
<classpathentry exported="true" kind="lib" path="C:/Users/costin/.gradle/cache/org.mockito/mockito-all/jars/mockito-all-1.8.5.jar" sourcepath="C:/Users/costin/.gradle/cache/org.mockito/mockito-all/sources/mockito-all-1.8.5-sources.jar"/>
<classpathentry exported="true" kind="lib" path="C:/Users/costin/.gradle/cache/junit/junit/jars/junit-4.8.1.jar" sourcepath="C:/Users/costin/.gradle/cache/junit/junit/sources/junit-4.8.1-sources.jar"/>
<classpathentry exported="true" kind="lib" path="C:/Users/costin/.gradle/cache/org.slf4j/jcl-over-slf4j/jars/jcl-over-slf4j-1.6.1.jar" sourcepath="C:/Users/costin/.gradle/cache/org.slf4j/jcl-over-slf4j/sources/jcl-over-slf4j-1.6.1-sources.jar"/>
<classpathentry exported="true" kind="lib" path="C:/Users/costin/.gradle/cache/commons-logging/commons-logging/jars/commons-logging-1.1.1.jar" sourcepath="C:/Users/costin/.gradle/cache/commons-logging/commons-logging/sources/commons-logging-1.1.1-sources.jar"/>
<classpathentry combineaccessrules="false" kind="src" path="/spring-data-core"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View File

@@ -1,2 +0,0 @@
*.log

View File

@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>spring-data-redis</name>
<comment/>
<projects/>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments/>
</buildCommand>
</buildSpec>
<links/>
</projectDescription>

View File

@@ -1,17 +0,0 @@
repositories {
mavenRepo name: "ext-snapshots", urls: "http://springframework.svn.sourceforge.net/svnroot/springframework/repos/repo-ext/"
}
dependencies {
compile project(":spring-data-keyvalue-core")
compile "redis.clients:jedis:$jedisVersion"
compile("org.jredis:jredis-anthonylauzon:$jredisVersion") { optional = true }
compile("org.idevlab:rjc:$rjcVersion") { optional = true }
compile("org.springframework:spring-oxm:$springVersion") { optional = true }
compile("commons-beanutils:commons-beanutils-core:1.8.3") { optional = true }
testCompile("javax.annotation:jsr250-api:1.0") { optional = true }
testCompile("com.thoughtworks.xstream:xstream:1.3") { optional = true }
}
sourceCompatibility = 1.6
targetCompatibility = 1.6

View File

@@ -1,13 +0,0 @@
# Dependencies properties
jedisVersion = 2.0.0
jredisVersion = 03122010
rjcVersion= 0.6.4
# Manifest properties
## OSGi ranges
spring.range = "[3.0.0, 4.0.0)"
jedis.range = "[2.0.0, 2.0.0]"
jackson.range = "[1.6, 2.0.0)"
rjc.range = "[0.6.4, 0.6.4]"

View File

@@ -1,5 +0,0 @@
/**
* Query package for Redis template.
*/
package org.springframework.data.keyvalue.redis.core.query;

View File

@@ -1,12 +0,0 @@
/**
* Package providing implementations for most of the {@code java.util} collections on top of Redis.
* <p/>
* For indexed collections, such as {@link java.util.List}, {@link java.util.Queue} or {@link java.util.Deque}
* consider {@link org.springframework.data.keyvalue.redis.support.collections.RedisList}.<p/>
* For collections without duplicates the obvious candidate is {@link org.springframework.data.keyvalue.redis.support.collections.RedisSet}. Use
* {@link org.springframework.data.keyvalue.redis.support.collections.RedisZSet} if a
* certain order is required.</p/>
* Lastly, for key/value associations {@link org.springframework.data.keyvalue.redis.support.collections.RedisMap} providing a Map-like abstraction on top of a Redis hash.
*/
package org.springframework.data.keyvalue.redis.support.collections;

View File

@@ -1,2 +0,0 @@
http\://www.springframework.org/schema/redis/spring-redis-1.0.xsd=org/springframework/data/keyvalue/redis/config/spring-redis-1.0.xsd
http\://www.springframework.org/schema/redis/spring-redis.xsd=org/springframework/data/keyvalue/redis/config/spring-redis-1.0.xsd

View File

@@ -1,107 +0,0 @@
# Spring Data support for Riak
The spring-data-riak module strives to make working with Riak painless by providing
the developer several different ways to easily access or store data using the Riak
Key/Value store.
## Recent Changes:
* 12/22/2010: Added async Map/Reduce support to AsyncRiakTemplate and Groovy DSL
* 12/20/2010: AsyncRiakTemplate and Groovy DSL
### Groovy DSL
One cool new feature just added is a Groovy DSL for data access using SDKV/Riak:
def riak = new RiakBuilder(riakTemplate)
def result = null
riak.set(bucket: "test", key: "test", qos: [dw: "all"], value: obj, wait: 3000L) {
completed(when: { it.integer == 12 }) { result = it.test }
completed { result = "otherwise" }
failed { it.printStackTrace() }
}
The Groovy DSL will respond to the following methods:
* set
* setAsBytes
* put
* get
* getAsBytes
* getAsType
* containsKey
* delete
* foreach
Each completed or failed closure can be accompanied by a "guard" closure. For example,
to process an entry differently, based on the type:
riak.get(bucket: "test", key: "test") {
completed(when: { it instanceof Map }) { processMap(it) }
completed(when: { it instanceof String }) { processString(it) }
completed(when: { it instanceof byte[] }) { processBytes(it) }
completed { result = "otherwise" }
failed { it.printStackTrace() }
}
You can nest them, of course. To insert data and then delete all keys from a bucket:
riak {
put(bucket: "test", value: [test: "value 1"])
put(bucket: "test", value: [test: "value 2"])
put(bucket: "test", value: [test: "value 3"])
foreach(bucket: "test") {
completed { v, meta ->
delete(bucket: meta.bucket, key: meta.key)
}
failed { it.printStackTrace() }
}
}
You can also use a "default" bucket by nesting your operations inside an arbitrary block. In
the example below, the `test{}` closure sets a default bucket of "test" and all the
subsequent operations check for this if a `bucket` is not specified (you can override the
default by specifying a `bucket` property on the operation itself).
The Groovy DSL for Riak now has Map/Reduce support. You build up a Map/Reduce job using the
closures shown in the example. You can pass static arguments to the phases, as well. You can
also specify a `wait` timeout on the `mapreduce` closure, just like with the other operations.
riak {
test {
put(value: [test: "value"])
put(value: [test: "value"])
put(value: [test: "value"])
put(value: [test: "value"])
mapreduce {
query {
map(arg: [test: "arg", alist: [1, 2, 3, 4]]) {
source "function(v){ return [1]; }"
}
reduce {
source "function(v){ return Riak.reduceSum(v); }"
}
}
completed { println "result $it" }
failed { it.printStackTrace() }
}
}
}
Some things to note here:
* The Groovy DSL utilizes the new AsyncRiakTemplate, so all closure calls happen
asynchronously. By default, the operation will block indefinitely. To not block at all
and continue on immediately, set the `wait` to `0`. To block until a specified timeout,
set the `wait` to the number of milliseconds to wait for the operation to complete before
timing out and throwing an exception.
* Callbacks are defined as either `completed` or `failed` closures. In addition to the
closure, you can define a "guard" closure, which is called before the main closure and
should return non-null or Boolean `true` if the closure should be executed or null or
Boolean `false` if the closure is to be skipped. This functionality is inspired by the
use of [the guard expression in Erlang case statements](http://en.wikibooks.org/wiki/Erlang_Programming/guards).

View File

@@ -1,9 +0,0 @@
dependencies {
compile project(":spring-data-keyvalue-core")
compile "org.codehaus.groovy:groovy-all:1.7.6"
compile "javax.mail:mail:1.4.1"
compile "javax.activation:activation:1.1.1"
compile "commons-cli:commons-cli:1.2"
compile "org.springframework:spring-web:$springVersion"
}

View File

@@ -1,38 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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;
import org.springframework.dao.DataAccessResourceFailureException;
/**
* @author J. Brisbin <jon@jbrisbin.com>
*/
public class DataStoreConnectionFailureException extends DataAccessResourceFailureException {
public static final long serialVersionUID = 1L;
public DataStoreConnectionFailureException(String msg) {
super(msg);
}
public DataStoreConnectionFailureException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -1,38 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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;
import org.springframework.dao.DataAccessException;
/**
* @author J. Brisbin <jon@jbrisbin.com>
*/
public class DataStoreOperationException extends DataAccessException {
public static final long serialVersionUID = 1L;
public DataStoreOperationException(String msg) {
super(msg);
}
public DataStoreOperationException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -1,47 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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.convert;
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";
}

View File

@@ -1,540 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.groovy.runtime.GStringImpl;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.ser.CustomSerializerFactory;
import org.codehaus.jackson.map.ser.ToStringSerializer;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.ConversionServiceFactory;
import org.springframework.data.keyvalue.riak.DataStoreOperationException;
import org.springframework.data.keyvalue.riak.convert.KeyValueStoreMetaData;
import org.springframework.data.keyvalue.riak.util.Ignore404sErrorHandler;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.client.support.RestGatewaySupport;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Base class for RiakTemplates that defines basic behaviour common to both kinds of templates
* (Key/Value and Bucket/Key/Value).
*
* @author J. Brisbin <jon@jbrisbin.com>
*/
public abstract class AbstractRiakTemplate extends RestGatewaySupport implements InitializingBean, BeanClassLoaderAware {
protected static final String RIAK_META_CLASSNAME = "X-Riak-Meta-ClassName";
protected static final String RIAK_VCLOCK = "X-Riak-Vclock";
/**
* Regex used to extract host, port, and prefix from the given URI.
*/
protected 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?
*/
protected final boolean groovyPresent = ClassUtils.isPresent(
"org.codehaus.groovy.runtime.GStringImpl",
getClass().getClassLoader());
/**
* For getting a <code>java.util.Date</code> from the Last-Modified header.
*/
protected static SimpleDateFormat httpDate = new SimpleDateFormat(
"EEE, d MMM yyyy HH:mm:ss z");
protected final Log log = LogFactory.getLog(getClass());
/**
* Client ID used by Riak to correlate updates.
*/
protected final String RIAK_CLIENT_ID = getClass().getName() + "/1.0";
/**
* 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;
/**
* 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 = new ArrayList<BucketKeyResolver>();
/**
* The default QosParameters to use for all operations through this template.
*/
protected QosParameters defaultQosParameters = null;
/**
* {@link java.util.concurrent.ExecutorService} to use for running asynchronous jobs.
*/
protected ExecutorService workerPool = Executors.newCachedThreadPool();
/**
* Default type to use when trying to deserialize objects and we can't otherwise tell what to
* do.
*/
protected Class<?> defaultType = String.class;
/**
* ClassLoader to use for saving/loading objects using the automatic converters.
*/
protected ClassLoader classLoader = null;
/**
* Take all the defaults.
*/
public AbstractRiakTemplate() {
setRestTemplate(new RestTemplate());
}
/**
* Use the specified {@link org.springframework.http.client.ClientHttpRequestFactory}.
*
* @param requestFactory
*/
public AbstractRiakTemplate(ClientHttpRequestFactory requestFactory) {
super(requestFactory);
setRestTemplate(new RestTemplate());
}
public ConversionService getConversionService() {
return conversionService;
}
/**
* Specify the conversion service to use.
*
* @param conversionService
*/
public void setConversionService(ConversionService conversionService) {
this.conversionService = conversionService;
}
public String getDefaultUri() {
return defaultUri;
}
public void setDefaultUri(String defaultUri) {
this.defaultUri = defaultUri;
}
public String getMapReduceUri() {
return mapReduceUri;
}
public void setMapReduceUri(String mapReduceUri) {
this.mapReduceUri = mapReduceUri;
}
public boolean isUseCache() {
return useCache;
}
public void setUseCache(boolean useCache) {
this.useCache = useCache;
}
public QosParameters getDefaultQosParameters() {
return defaultQosParameters;
}
public void setDefaultQosParameters(QosParameters defaultQosParameters) {
this.defaultQosParameters = defaultQosParameters;
}
public ExecutorService getWorkerPool() {
return workerPool;
}
public void setWorkerPool(ExecutorService workerPool) {
this.workerPool = workerPool;
}
public void setIgnoreNotFound(boolean b) {
if (b) {
getRestTemplate().setErrorHandler(new Ignore404sErrorHandler());
} else {
if (getRestTemplate().getErrorHandler() instanceof Ignore404sErrorHandler) {
getRestTemplate().setErrorHandler(new DefaultResponseErrorHandler());
}
}
}
public boolean getIgnoreNotFound() {
return (getRestTemplate().getErrorHandler() instanceof Ignore404sErrorHandler);
}
/**
* Get the default type to use if none can be inferred.
*
* @return
*/
public Class<?> getDefaultType() {
return defaultType;
}
/**
* Set the default type to use if none can be inferred.
*
* @param defaultType
*/
public void setDefaultType(Class<?> defaultType) {
this.defaultType = defaultType;
}
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
public String getHost() {
Matcher m = prefix.matcher(defaultUri);
if (m.matches()) {
return m.group(1);
}
return "localhost";
}
public Integer getPort() {
Matcher m = prefix.matcher(defaultUri);
if (m.matches()) {
return new Integer(m.group(2));
}
return 8098;
}
/**
* Extract the prefix from the URI for use in creating links.
*
* @return
*/
public String getPrefix() {
Matcher m = prefix.matcher(defaultUri);
if (m.matches()) {
return "/" + m.group(3);
}
return "/riak";
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(conversionService,
"Must specify a valid ConversionService.");
List<HttpMessageConverter<?>> converters = getRestTemplate().getMessageConverters();
ObjectMapper mapper = new ObjectMapper();
CustomSerializerFactory fac = new CustomSerializerFactory();
if (groovyPresent) {
// Native conversion for Groovy GString objects
fac.addSpecificMapping(GStringImpl.class, ToStringSerializer.instance);
}
mapper.setSerializerFactory(fac);
for (HttpMessageConverter converter : converters) {
if (converter instanceof MappingJacksonHttpMessageConverter) {
((MappingJacksonHttpMessageConverter) converter).setObjectMapper(
mapper);
}
}
}
/*----------------- Utilities -----------------*/
@SuppressWarnings({"unchecked"})
protected BucketKeyPair resolveBucketKeyPair(Object key, Object val) {
BucketKeyResolver resolver = null;
for (BucketKeyResolver r : bucketKeyResolvers) {
if (r.canResolve(key)) {
resolver = r;
break;
}
}
if (null == resolver) {
resolver = new SimpleBucketKeyResolver();
}
BucketKeyPair bucketKeyPair = resolver.resolve(key);
if (null == bucketKeyPair.getBucket() && null != val) {
// No bucket specified, check for an annotation that specified bucket name.
Annotation meta = (val instanceof Class ? (Class) val : val.getClass()).getAnnotation(
org.springframework.data.keyvalue.riak.convert.KeyValueStoreMetaData.class);
if (null != meta) {
String bucket = ((KeyValueStoreMetaData) meta).bucket();
if (null != bucket) {
return new SimpleBucketKeyPair<String, Object>(bucket,
bucketKeyPair.getKey());
}
}
}
return bucketKeyPair;
}
protected MediaType extractMediaType(Object value) {
MediaType mediaType = (value instanceof byte[] ? MediaType.APPLICATION_OCTET_STREAM : MediaType.APPLICATION_JSON);
if (null != value && value.getClass().getAnnotations().length > 0) {
KeyValueStoreMetaData meta = value.getClass()
.getAnnotation(KeyValueStoreMetaData.class);
if (null != meta) {
// Use the media type specified on the annotation.
mediaType = MediaType.parseMediaType(meta.mediaType());
}
}
return mediaType;
}
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")) {
prop = httpDate.parse(val.get(0));
}
} catch (ParseException e) {
log.error(e.getMessage(), e);
}
if (entry.getKey().equals("Link")) {
List<String> links = new ArrayList<String>();
for (String link : entry.getValue()) {
String[] parts = link.split(",");
for (String part : parts) {
String s = part.replaceAll("<(.+)>; rel=\"(\\S+)\"[,]?", "").trim();
if (!"".equals(s)) {
links.add(s);
}
}
}
props.put("Link", links);
} else {
props.put(entry.getKey().toString(), prop);
}
}
props.put("ETag", headers.getETag());
RiakMetaData meta = new RiakMetaData(headers.getContentType(), props);
return meta;
}
@SuppressWarnings({"unchecked"})
protected <T> RiakValue<T> extractValue(final ResponseEntity<?> response, Class<?> origType,
Class<T> requiredType) throws
IOException {
if (response.hasBody()) {
RiakMetaData meta = extractMetaData(response.getHeaders());
Object o = response.getBody();
if (!origType.equals(requiredType)) {
if (conversionService.canConvert(origType, requiredType)) {
o = conversionService.convert(o, requiredType);
} else {
if (o instanceof byte[] || o instanceof String) {
// Peek inside, see if it's a string of something we recognize
String s = (o instanceof byte[] ? new String((byte[]) o) : (String) o);
if (s.charAt(0) == '{' || s.charAt(0) == '[') {
// Looks like it might be a JSON string. Use the JSON converter
for (HttpMessageConverter conv : getRestTemplate().getMessageConverters()) {
if (conv instanceof MappingJacksonHttpMessageConverter) {
o = conv.read(requiredType, new HttpInputMessage() {
public InputStream getBody() throws IOException {
Object body = response.getBody();
return new ByteArrayInputStream(
(body instanceof byte[] ? (byte[]) body : ((String) body)
.getBytes()));
}
public HttpHeaders getHeaders() {
return response.getHeaders();
}
});
break;
}
}
}
} else {
throw new DataStoreOperationException(
"Cannot convert object of type " + origType + " to type " + requiredType);
}
}
}
return new RiakValue<T>((T) o, meta);
}
return null;
}
@SuppressWarnings({"unchecked"})
protected <K, T> T checkCache(K key, Class<T> requiredType) {
BucketKeyPair bucketKeyPair = resolveBucketKeyPair(key, requiredType);
RiakValue<?> obj = cache.get(bucketKeyPair);
if (null != obj) {
String bucketName = (null != bucketKeyPair.getBucket() ? bucketKeyPair.getBucket()
.toString() : requiredType.getName());
RestTemplate restTemplate = getRestTemplate();
try {
HttpHeaders resp = restTemplate.headForHeaders(defaultUri,
bucketName,
bucketKeyPair.getKey());
if (!obj.getMetaData()
.getProperties()
.get("ETag")
.toString()
.equals(resp.getETag())) {
obj = null;
} else {
if (log.isDebugEnabled()) {
log.debug("Returning CACHED object: " + obj);
}
}
} catch (ResourceAccessException ignored) {
return null;
}
}
if (null != obj && obj.getClass() == requiredType) {
return (T) obj.get();
} else {
return null;
}
}
/**
* Get a string that represents the QOS parameters, taken either from the specified object or
* from the template defaults.
*
* @param qosParams
* @return
*/
protected String extractQosParameters(QosParameters qosParams) {
List<String> params = new LinkedList<String>();
if (null != qosParams.getReadThreshold()) {
params.add(String.format("r=%s", qosParams.<Object>getReadThreshold()));
} else if (null != defaultQosParameters && null != defaultQosParameters
.getReadThreshold()) {
params.add(String.format("r=%s", defaultQosParameters.getReadThreshold()));
}
if (null != qosParams.getWriteThreshold()) {
params.add(String.format("w=%s", qosParams.<Object>getWriteThreshold()));
} else if (null != defaultQosParameters && null != defaultQosParameters
.getWriteThreshold()) {
params.add(String.format("w=%s", defaultQosParameters.getWriteThreshold()));
}
if (null != qosParams.getDurableWriteThreshold()) {
params.add(String.format("dw=%s", qosParams.<Object>getDurableWriteThreshold()));
} else if (null != defaultQosParameters && null != defaultQosParameters
.getDurableWriteThreshold()) {
params.add(String.format("dw=%s", defaultQosParameters.getDurableWriteThreshold()));
}
return (params.size() > 0 ? "?" + StringUtils.collectionToDelimitedString(
params,
"&") : "");
}
protected HttpHeaders defaultHeaders(Map<String, ?> metadata) {
HttpHeaders headers = new HttpHeaders();
headers.set("X-Riak-ClientId", RIAK_CLIENT_ID);
if (null != metadata) {
for (Map.Entry<String, ?> entry : metadata.entrySet()) {
Object o = entry.getValue();
headers.set(entry.getKey(), (null != o ? o.toString() : null));
}
}
return headers;
}
protected <B, K> Class<?> getType(B bucket, K key) {
return getType(bucket, key, getClass().getClassLoader());
}
protected <B, K> Class<?> getType(B bucket, K key, ClassLoader classLoader) {
Class<?> clazz = null;
try {
HttpHeaders headers = getRestTemplate().headForHeaders(defaultUri, bucket, key);
if (null != headers) {
String s = headers.getFirst(RIAK_META_CLASSNAME);
if (null != s) {
try {
if (null != classLoader) {
clazz = Class.forName(s, false, classLoader);
} else {
clazz = Class.forName(s);
}
} catch (ClassNotFoundException ignored) {
}
}
}
if (null == clazz) {
if (headers.getContentType().equals(MediaType.APPLICATION_JSON)) {
clazz = Map.class;
} else if (headers.getContentType().equals(MediaType.TEXT_PLAIN)) {
clazz = String.class;
} else {
// handle as bytes
log.error("Need to handle bytes!");
clazz = byte[].class;
}
}
} catch (ResourceAccessException notFound) {
clazz = String.class;
}
return clazz;
}
}

View File

@@ -1,166 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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 java.util.Map;
import java.util.concurrent.Future;
/**
* An asynchronous version of {@link BucketKeyValueStoreOperations}.
*
* @author J. Brisbin <jon@jbrisbin.com>
*/
public interface AsyncBucketKeyValueStoreOperations {
/**
* Put an object in Riak at a specific bucket and key and invoke callback with the value
* pulled back out of Riak after the update, which contains full headers and metadata.
*
* @param bucket
* @param key
* @param value
* @param callback Called with the update value pulled from Riak
*/
<B, K, V, R> Future<?> set(B bucket, K key, V value, AsyncKeyValueStoreOperation<V, R> callback);
/**
* @param bucket
* @param key
* @param value
* @param qosParams
* @return
*/
<B, K, V, R> Future<?> set(B bucket, K key, V value, QosParameters qosParams, AsyncKeyValueStoreOperation<V, R> callback);
/**
* @param bucket
* @param key
* @param value
* @return
*/
<B, K, R> Future<?> setAsBytes(B bucket, K key, byte[] value, AsyncKeyValueStoreOperation<byte[], R> callback);
/**
* @param bucket
* @param key
* @param value
* @param qosParams
* @return
*/
<B, K, R> Future<?> setAsBytes(B bucket, K key, byte[] value, QosParameters qosParams, AsyncKeyValueStoreOperation<byte[], R> callback);
/**
* @param bucket
* @param key
* @param value
* @param metaData
* @return
*/
<B, K, V, R> Future<?> setWithMetaData(B bucket, K key, V value, Map<String, String> metaData, AsyncKeyValueStoreOperation<V, R> callback);
/**
* @param bucket
* @param key
* @param value
* @param metaData
* @param qosParams
* @return
*/
<B, K, V, R> Future<?> setWithMetaData(B bucket, K key, V value, Map<String, String> metaData, QosParameters qosParams, AsyncKeyValueStoreOperation<V, R> callback);
/**
* @param bucket
* @param key
* @return
*/
<B, K, V, R> Future<?> get(B bucket, K key, AsyncKeyValueStoreOperation<V, R> callback);
/**
* @param bucket
* @param key
* @return
*/
<B, K, R> Future<?> getAsBytes(B bucket, K key, AsyncKeyValueStoreOperation<byte[], R> callback);
/**
* @param bucket
* @param key
* @param requiredType
* @return
*/
<B, K, T, R> Future<?> getAsType(B bucket, K key, Class<T> requiredType, AsyncKeyValueStoreOperation<T, R> callback);
/**
* @param bucket
* @param key
* @param value
* @return
*/
<B, K, V, R> Future<?> getAndSet(B bucket, K key, V value, AsyncKeyValueStoreOperation<V, R> callback);
/**
* @param bucket
* @param key
* @param value
* @return
*/
<B, K, R> Future<?> getAndSetAsBytes(B bucket, K key, byte[] value, AsyncKeyValueStoreOperation<byte[], R> callback);
/**
* @param bucket
* @param key
* @param value
* @param requiredType
* @return
*/
<B, K, V, T, R> Future<?> getAndSetAsType(B bucket, K key, V value, Class<T> requiredType, AsyncKeyValueStoreOperation<T, R> callback);
/**
* @param bucket
* @param key
* @param value
* @return
*/
<B, K, V, R> Future<?> setIfKeyNonExistent(B bucket, K key, V value, AsyncKeyValueStoreOperation<V, R> callback);
/**
* @param bucket
* @param key
* @param value
* @return
*/
<B, K, R> Future<?> setIfKeyNonExistentAsBytes(B bucket, K key, byte[] value, AsyncKeyValueStoreOperation<byte[], R> callback);
/**
* @param bucket
* @param key
* @return
*/
<B, K, R> Future<?> containsKey(B bucket, K key, AsyncKeyValueStoreOperation<Boolean, R> callback);
/**
* Delete a specific entry from this data store.
*
* @param bucket
* @param key
* @return
*/
<B, K, R> Future<?> delete(B bucket, K key, AsyncKeyValueStoreOperation<Boolean, R> callback);
}

View File

@@ -1,29 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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;
/**
* @author J. Brisbin <jon@jbrisbin.com>
*/
public interface AsyncKeyValueStoreOperation<V, T> {
T completed(KeyValueStoreMetaData meta, V result);
T failed(Throwable error);
}

View File

@@ -1,630 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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.dao.DataAccessResourceFailureException;
import org.springframework.data.keyvalue.riak.DataStoreOperationException;
import org.springframework.data.keyvalue.riak.mapreduce.AsyncMapReduceOperations;
import org.springframework.data.keyvalue.riak.mapreduce.MapReduceJob;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.util.Assert;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
/**
* An implementation of {@link AsyncBucketKeyValueStoreOperations} and {@link
* AsyncMapReduceOperations} for the Riak datastore.
* <p/>
* To use the AsyncRiakTemplate, create a singleton in your Spring application-context.xml:
* <pre><code>
* &lt;bean id="riak" class="org.springframework.data.keyvalue.riak.core.AsyncRiakTemplate"
* 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 callback = [
* completed: { v, meta ->
* ... do something with results ...
* },
* failed: { err ->
* }
* ] as AsyncKeyValueStoreOperation
* def obj = new TestObject(name: "My Name", age: 40)
* def future = riak.set("mybucket", "mykey", obj, callback)
* ... this runs asynchronously, so do other work ...
* def name = future.get().name
* println "Hello $name!"
* </code></pre>
*
* @author J. Brisbin <jon@jbrisbin.com>
*/
public class AsyncRiakTemplate extends AbstractRiakTemplate implements AsyncBucketKeyValueStoreOperations, AsyncMapReduceOperations {
protected AsyncKeyValueStoreOperation<Throwable, Object> defaultErrorHandler = new LoggingErrorHandler();
public AsyncRiakTemplate() {
super();
}
public AsyncRiakTemplate(ClientHttpRequestFactory requestFactory) {
super(requestFactory);
}
public AsyncKeyValueStoreOperation<Throwable, Object> getDefaultErrorHandler() {
return defaultErrorHandler;
}
public void setDefaultErrorHandler(
AsyncKeyValueStoreOperation<Throwable, Object> defaultErrorHandler) {
this.defaultErrorHandler = defaultErrorHandler;
}
public <B, K, V, R> Future<?> set(B bucket, K key, V value,
AsyncKeyValueStoreOperation<V, R> callback) {
return setWithMetaData(bucket, key, value, null, null, callback);
}
public <B, K, V, R> Future<?> set(B bucket, K key, V value, QosParameters qosParams,
AsyncKeyValueStoreOperation<V, R> callback) {
return setWithMetaData(bucket, key, value, null, qosParams, callback);
}
public <B, K, R> Future<?> setAsBytes(B bucket, K key, byte[] value,
AsyncKeyValueStoreOperation<byte[], R> callback) {
return setWithMetaData(bucket, key, value, null, null, callback);
}
@SuppressWarnings({"unchecked"})
public <B, K, V, R> Future<V> setWithMetaData(B bucket, K key, V value,
Map<String, String> metaData,
QosParameters qosParams,
AsyncKeyValueStoreOperation<V, R> callback) {
String bucketName = (null != bucket ? bucket.toString() : value.getClass().getName());
// Get a key name that may or may not include the QOS parameters.
Assert.notNull(key, "Cannot use a <NULL> key.");
String keyName = (null != qosParams ? key.toString() + extractQosParameters(qosParams) : key
.toString());
KeyValueStoreMetaData origMeta = getMetaData(bucket, keyName);
String vclock = null;
if (null != origMeta) {
Map<String, Object> mprops = origMeta.getProperties();
if (null != mprops) {
Object o = mprops.get(RIAK_VCLOCK);
if (null != o) {
vclock = o.toString();
}
}
}
HttpHeaders headers = defaultHeaders(metaData);
headers.setContentType(extractMediaType(value));
if (null != vclock) {
headers.set(RIAK_VCLOCK, vclock);
}
headers.set(RIAK_META_CLASSNAME, value.getClass().getName());
HttpEntity<V> entity = new HttpEntity<V>(value, headers);
return (Future<V>) workerPool.submit(new AsyncPost<V, R>(bucketName,
keyName,
entity,
callback));
}
public <B, V, R> Future<V> put(B bucket, V value,
AsyncKeyValueStoreOperation<V, R> callback) {
return put(bucket, value, null, null, callback);
}
public <B, V, R> Future<V> put(B bucket, V value, Map<String, String> metaData,
AsyncKeyValueStoreOperation<V, R> callback) {
return put(bucket, value, metaData, null, callback);
}
@SuppressWarnings({"unchecked"})
public <B, V, R> Future<V> put(B bucket, V value, Map<String, String> metaData,
QosParameters qosParams,
AsyncKeyValueStoreOperation<V, R> callback) {
Assert.notNull(bucket, "Bucket cannot be null");
String bucketName = (null != qosParams ? bucket.toString() + extractQosParameters(
qosParams) : bucket
.toString());
HttpHeaders headers = defaultHeaders(metaData);
headers.setContentType(extractMediaType(value));
headers.set(RIAK_META_CLASSNAME, value.getClass().getName());
HttpEntity<V> entity = new HttpEntity<V>(value, headers);
return (Future<V>) workerPool.submit(new AsyncPut<V, R>(bucketName, entity, callback));
}
public <B, K, V, R> Future<?> get(B bucket, K key,
AsyncKeyValueStoreOperation<V, R> callback) {
return getWithMetaData(bucket, key, null, callback);
}
public <B, K> RiakMetaData getMetaData(B bucket, K key) {
RestTemplate restTemplate = getRestTemplate();
HttpHeaders headers;
try {
headers = restTemplate.headForHeaders(defaultUri, bucket, key);
RiakMetaData meta = extractMetaData(headers);
meta.setBucket((null != bucket ? bucket.toString() : null));
meta.setKey((null != key ? key.toString() : null));
return meta;
} catch (ResourceAccessException e) {
} catch (IOException e) {
throw new DataAccessResourceFailureException(e.getMessage(), e);
}
return null;
}
@SuppressWarnings({"unchecked"})
public <B, R> Future<?> getBucketSchema(B bucket, QosParameters qosParams,
final AsyncKeyValueStoreOperation<Map<String, Object>, R> callback) {
Assert.notNull(bucket, "Bucket cannot be null");
Assert.notNull(callback, "Callback cannot be null");
String bucketName = (null != qosParams ? bucket.toString() + extractQosParameters(
qosParams) : bucket
.toString());
return workerPool.submit(new AsyncGet(bucketName,
"?keys=true",
Map.class,
new AsyncKeyValueStoreOperation<Object, Object>() {
@SuppressWarnings({"unchecked"})
public Object completed(KeyValueStoreMetaData meta, Object result) {
return callback.completed(meta, (Map<String, Object>) result);
}
public Object failed(Throwable error) {
return callback.failed(error);
}
}));
}
@SuppressWarnings({"unchecked"})
public <B, K, T, R> Future<?> getWithMetaData(B bucket, K key, Class<T> requiredType,
AsyncKeyValueStoreOperation<T, R> callback) {
Assert.notNull(key, "Cannot use a null key.");
Assert.notNull(callback, "Callback cannot be null");
String bucketName = (null != bucket ? bucket.toString() : requiredType.getName());
if (null == requiredType) {
requiredType = (Class<T>) getType(bucketName, key.toString());
}
return workerPool.submit(new AsyncGet<T, R>(bucketName,
key.toString(),
requiredType,
callback));
}
public <B, K, R> Future<?> getAsBytes(B bucket, K key,
AsyncKeyValueStoreOperation<byte[], R> callback) {
return getWithMetaData(bucket, key, byte[].class, callback);
}
public <B, K, T, R> Future<?> getAsType(B bucket, K key, Class<T> requiredType,
AsyncKeyValueStoreOperation<T, R> callback) {
return getWithMetaData(bucket, key, requiredType, callback);
}
public <B, K, V, R> Future<?> getAndSet(final B bucket, final K key, final V value,
final AsyncKeyValueStoreOperation<V, R> callback) {
final List<Future<?>> futures = new ArrayList<Future<?>>();
try {
getWithMetaData(bucket, key, null, new AsyncKeyValueStoreOperation<Object, Object>() {
@SuppressWarnings({"unchecked"})
public Object completed(KeyValueStoreMetaData meta, Object result) {
futures.add(setWithMetaData(bucket, key, value, null, null, null));
return callback.completed(meta, (V) result);
}
public Object failed(Throwable error) {
return callback.failed(error);
}
}).get();
} catch (InterruptedException e) {
log.error(e.getMessage(), e);
} catch (ExecutionException e) {
log.error(e.getMessage(), e);
}
return futures.size() > 0 ? futures.get(0) : null;
}
public <B, K, R> Future<?> getAndSetAsBytes(B bucket, K key, byte[] value,
AsyncKeyValueStoreOperation<byte[], R> callback) {
return getAndSet(bucket, key, value, callback);
}
public <B, K, V, T, R> Future<?> getAndSetAsType(final B bucket, final K key, final V value,
final Class<T> requiredType,
final AsyncKeyValueStoreOperation<T, R> callback) {
final List<Future<?>> futures = new ArrayList<Future<?>>();
getWithMetaData(bucket, key, requiredType, new AsyncKeyValueStoreOperation<T, R>() {
@SuppressWarnings({"unchecked"})
public R completed(KeyValueStoreMetaData meta, T result) {
try {
setWithMetaData(bucket, key, value, null, null, null).get();
return callback.completed(meta, result);
} catch (InterruptedException e) {
return callback.failed(e);
} catch (ExecutionException e) {
return callback.failed(e);
}
}
public R failed(Throwable error) {
return callback.failed(error);
}
});
return futures.size() > 0 ? futures.get(0) : null;
}
public <B, K, V, R> Future<?> setIfKeyNonExistent(final B bucket, final K key, final V value,
final AsyncKeyValueStoreOperation<V, R> callback) {
return containsKey(bucket, key, new AsyncKeyValueStoreOperation<Boolean, Object>() {
public Object completed(KeyValueStoreMetaData meta, Boolean result) {
if (!result) {
return setWithMetaData(bucket, key, value, null, null, callback);
} else {
return null;
}
}
public Object failed(Throwable error) {
return callback.failed(error);
}
});
}
public <B, K, R> Future<?> setIfKeyNonExistentAsBytes(final B bucket, final K key,
final byte[] value,
final AsyncKeyValueStoreOperation<byte[], R> callback) {
return containsKey(bucket, key, new AsyncKeyValueStoreOperation<Boolean, Object>() {
public Object completed(KeyValueStoreMetaData meta, Boolean result) {
if (!result) {
return setWithMetaData(bucket, key, value, null, null, callback);
} else {
return null;
}
}
public Object failed(Throwable error) {
return callback.failed(error);
}
});
}
@SuppressWarnings({"unchecked"})
public <B, K, R> Future<?> containsKey(B bucket, K key,
final AsyncKeyValueStoreOperation<Boolean, R> callback) {
Assert.notNull(bucket, "Bucket cannot be null when checking for existence.");
Assert.notNull(key, "Key cannot be null when checking for existence");
return workerPool.submit(new AsyncHead(bucket.toString(),
key.toString(),
new AsyncKeyValueStoreOperation<HttpHeaders, Object>() {
public Object completed(KeyValueStoreMetaData meta, HttpHeaders result) {
return callback.completed(null, (null != result));
}
public Object failed(Throwable error) {
return callback.failed(error);
}
}));
}
@SuppressWarnings({"unchecked"})
public <B, K, R> Future<?> delete(B bucket, K key,
AsyncKeyValueStoreOperation<Boolean, R> callback) {
Assert.notNull(bucket, "Bucket cannot be null when deleting.");
Assert.notNull(key, "Key cannot be null when deleting.");
return workerPool.submit(new AsyncDelete(bucket.toString(), key.toString(), callback));
}
public <B, K, R> Future<?> setAsBytes(B bucket, K key, byte[] value, QosParameters qosParams,
AsyncKeyValueStoreOperation<byte[], R> callback) {
return setWithMetaData(bucket, key, value, null, qosParams, callback);
}
public <B, K, V, R> Future<?> setWithMetaData(B bucket, K key, V value,
Map<String, String> metaData,
AsyncKeyValueStoreOperation<V, R> callback) {
return setWithMetaData(bucket, key, value, metaData, null, callback);
}
/* ---------------- Map/Reduce ---------------- */
@SuppressWarnings({"unchecked"})
public <R> Future<?> execute(MapReduceJob job,
AsyncKeyValueStoreOperation<List<?>, R> callback) {
HttpHeaders headers = defaultHeaders(null);
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> json = new HttpEntity<String>(job.toJson(), headers);
return workerPool.submit(new AsyncMapReduce(json, callback));
}
/* ---------------- Runnable helpers ---------------- */
protected class AsyncPut<V, R> implements Callable {
private String bucket;
private HttpEntity<V> entity = null;
private AsyncKeyValueStoreOperation<V, R> callback = null;
public AsyncPut(String bucket, HttpEntity<V> entity,
AsyncKeyValueStoreOperation<V, R> callback) {
this.bucket = bucket;
this.entity = entity;
this.callback = callback;
}
public R call() throws Exception {
try {
URI location = getRestTemplate().postForLocation(defaultUri, entity, bucket, "");
String path = location.getPath();
String key = path.substring(path.lastIndexOf("/") + 1);
HttpHeaders headers = getRestTemplate().headForHeaders(defaultUri, bucket, key);
if (null != callback) {
RiakMetaData meta = extractMetaData(headers);
meta.setBucket((null != bucket ? bucket.toString() : null));
meta.setKey((null != key ? key.toString() : null));
return callback.completed(meta, entity.getBody());
}
} catch (Throwable t) {
DataStoreOperationException dsoe = new DataStoreOperationException(t.getMessage(), t);
if (null != callback) {
return callback.failed(dsoe);
} else {
defaultErrorHandler.failed(dsoe);
}
}
return null;
}
}
protected class AsyncPost<V, R> implements Callable {
private String bucket;
private String key;
private HttpEntity<V> entity = null;
private AsyncKeyValueStoreOperation<V, R> callback = null;
public AsyncPost(String bucket, String key, HttpEntity<V> entity,
AsyncKeyValueStoreOperation<V, R> callback) {
this.bucket = bucket;
this.key = key;
this.entity = entity;
this.callback = callback;
}
@SuppressWarnings({"unchecked"})
public R call() throws Exception {
try {
HttpEntity<?> result = getRestTemplate().postForEntity(defaultUri,
entity,
(entity.getBody() instanceof byte[] ? byte[].class : entity.getBody().getClass()),
bucket,
key + "?returnbody=true");
if (log.isDebugEnabled()) {
log.debug(String.format("PUT object: bucket=%s, key=%s, value=%s",
bucket,
key,
entity));
}
if (null != callback) {
RiakMetaData meta = extractMetaData(result.getHeaders());
meta.setBucket((null != bucket ? bucket.toString() : null));
meta.setKey((null != key ? key.toString() : null));
return callback.completed(meta, (V) result.getBody());
}
} catch (Throwable t) {
DataStoreOperationException dsoe = new DataStoreOperationException(t.getMessage(), t);
if (null != callback) {
return callback.failed(dsoe);
} else {
defaultErrorHandler.failed(dsoe);
}
}
return null;
}
}
protected class AsyncMapReduce<R> implements Callable {
private HttpEntity<String> entity = null;
private AsyncKeyValueStoreOperation<List<?>, R> callback = null;
public AsyncMapReduce(HttpEntity<String> entity,
AsyncKeyValueStoreOperation<List<?>, R> callback) {
this.entity = entity;
this.callback = callback;
}
@SuppressWarnings({"unchecked"})
public R call() throws Exception {
try {
HttpEntity<List> result = getRestTemplate().postForEntity(mapReduceUri,
entity,
List.class);
if (log.isDebugEnabled()) {
log.debug(String.format("M/R: json=%s", entity.getBody()));
}
if (null != callback) {
RiakMetaData meta = extractMetaData(result.getHeaders());
return callback.completed(meta, result.getBody());
}
} catch (Throwable t) {
DataStoreOperationException dsoe = new DataStoreOperationException(t.getMessage(), t);
if (null != callback) {
return callback.failed(dsoe);
} else {
defaultErrorHandler.failed(dsoe);
}
}
return null;
}
}
protected class AsyncGet<T, R> implements Callable {
private String bucket;
private String key;
private Class<T> requiredType;
private AsyncKeyValueStoreOperation<T, R> callback = null;
public AsyncGet(String bucket, String key, Class<T> requiredType,
AsyncKeyValueStoreOperation<T, R> callback) {
this.bucket = bucket;
this.key = key;
this.requiredType = requiredType;
this.callback = callback;
}
public R call() throws Exception {
try {
ResponseEntity<T> result = getRestTemplate().getForEntity(defaultUri,
requiredType,
bucket,
key);
if (result.hasBody()) {
RiakMetaData meta = extractMetaData(result.getHeaders());
meta.setBucket((null != bucket ? bucket.toString() : null));
meta.setKey((null != key ? key.toString() : null));
RiakValue<T> val = new RiakValue<T>(result.getBody(), meta);
if (useCache) {
cache.put(new SimpleBucketKeyPair<Object, Object>(bucket, key), val);
}
if (null != callback) {
return callback.completed(meta, val.get());
}
if (log.isDebugEnabled()) {
log.debug(String.format("GET object: bucket=%s, key=%s, type=%s",
bucket,
key,
requiredType.getName()));
}
}
} catch (Throwable t) {
DataStoreOperationException dsoe = new DataStoreOperationException(t.getMessage(), t);
if (null != callback) {
return callback.failed(dsoe);
} else {
defaultErrorHandler.failed(dsoe);
}
}
return null;
}
}
protected class AsyncHead<V, R> implements Callable {
private String bucket;
private String key;
private AsyncKeyValueStoreOperation<HttpHeaders, R> callback = null;
public AsyncHead(String bucket, String key,
AsyncKeyValueStoreOperation<HttpHeaders, R> callback) {
this.bucket = bucket;
this.key = key;
this.callback = callback;
}
public R call() throws Exception {
try {
HttpHeaders headers = getRestTemplate().headForHeaders(defaultUri, bucket, key);
if (null != headers) {
if (null != callback) {
return callback.completed(null, headers);
}
}
} catch (Throwable t) {
DataStoreOperationException dsoe = new DataStoreOperationException(t.getMessage(), t);
if (null != callback) {
return callback.failed(dsoe);
} else {
defaultErrorHandler.failed(dsoe);
}
}
return null;
}
}
protected class AsyncDelete<V, R> implements Callable {
private String bucket;
private String key;
private AsyncKeyValueStoreOperation<Boolean, R> callback = null;
public AsyncDelete(String bucket, String key,
AsyncKeyValueStoreOperation<Boolean, R> callback) {
this.bucket = bucket;
this.key = key;
this.callback = callback;
}
public R call() throws Exception {
try {
getRestTemplate().delete(defaultUri, bucket, key);
if (null != callback) {
return callback.completed(null, true);
}
} catch (Throwable t) {
DataStoreOperationException dsoe = new DataStoreOperationException(t.getMessage(), t);
if (null != callback) {
return callback.failed(dsoe);
} else {
defaultErrorHandler.failed(dsoe);
}
}
return null;
}
}
protected class LoggingErrorHandler implements AsyncKeyValueStoreOperation<Throwable, Object> {
public Object completed(KeyValueStoreMetaData meta, Throwable result) {
return null;
}
public Object failed(Throwable error) {
log.error(error.getMessage(), error);
return null;
}
}
}

View File

@@ -1,43 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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;
/**
* 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();
}

View File

@@ -1,45 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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;
/**
* A generic interface to a resolver to turn a single object into a {@link
* org.springframework.data.keyvalue.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);
}

View File

@@ -1,205 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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 java.util.Map;
/**
* @author J. Brisbin <jon@jbrisbin.com>
*/
public interface BucketKeyValueStoreOperations {
/**
* Variant of {@link org.springframework.data.keyvalue.riak.core.KeyValueStoreOperations#set(Object,
* Object)} that takes a discreet bucket and key pair.
*
* @param bucket
* @param key
* @param value
* @return
*/
<B, K, V> BucketKeyValueStoreOperations set(B bucket, K key, V value);
/**
* Variant of {@link org.springframework.data.keyvalue.riak.core.KeyValueStoreOperations#set(Object,
* Object, QosParameters)} that takes a discreet bucket and key pair.
*
* @param bucket
* @param key
* @param value
* @param qosParams
* @return
*/
<B, K, V> BucketKeyValueStoreOperations set(B bucket, K key, V value, QosParameters qosParams);
/**
* Variant of {@link org.springframework.data.keyvalue.riak.core.KeyValueStoreOperations#setAsBytes(Object,
* byte[])} that takes a discreet bucket and key pair.
*
* @param bucket
* @param key
* @param value
* @return
*/
<B, K> BucketKeyValueStoreOperations setAsBytes(B bucket, K key, byte[] value);
/**
* Variant of {@link org.springframework.data.keyvalue.riak.core.KeyValueStoreOperations#setWithMetaData(Object,
* Object, java.util.Map, QosParameters)} that takes a discreet bucket and key pair.
*
* @param bucket
* @param key
* @param value
* @param metaData
* @param qosParams
* @return
*/
<B, K, V> BucketKeyValueStoreOperations setWithMetaData(B bucket, K key, V value, Map<String, String> metaData, QosParameters qosParams);
/**
* Variant of {@link org.springframework.data.keyvalue.riak.core.KeyValueStoreOperations#get(Object)}
* that takes a discreet bucket and key pair.
*
* @param bucket
* @param key
* @return
*/
<B, K, V> V get(B bucket, K key);
/**
* Variant of {@link org.springframework.data.keyvalue.riak.core.KeyValueStoreOperations#getAsBytes(Object)}
* that takes a discreet bucket and key pair.
*
* @param bucket
* @param key
* @return
*/
<B, K> byte[] getAsBytes(B bucket, K key);
/**
* Variant of {@link org.springframework.data.keyvalue.riak.core.KeyValueStoreOperations#getAsType(Object,
* Class)} that takes a discreet bucket and key pair.
*
* @param bucket
* @param key
* @param requiredType
* @return
*/
<B, K, T> T getAsType(B bucket, K key, Class<T> requiredType);
/**
* Variant of {@link org.springframework.data.keyvalue.riak.core.KeyValueStoreOperations#getAndSet(Object,
* Object)} that takes a discreet bucket and key pair.
*
* @param bucket
* @param key
* @param value
* @return
*/
<B, K, V> V getAndSet(B bucket, K key, V value);
/**
* Variant of {@link org.springframework.data.keyvalue.riak.core.KeyValueStoreOperations#getAndSetAsBytes(Object,
* byte[])} that takes a discreet bucket and key pair.
*
* @param bucket
* @param key
* @param value
* @return
*/
<B, K> byte[] getAndSetAsBytes(B bucket, K key, byte[] value);
/**
* Variant of {@link org.springframework.data.keyvalue.riak.core.KeyValueStoreOperations#getAndSetAsType(Object,
* Object, Class)} that takes a discreet bucket and key pair.
*
* @param bucket
* @param key
* @param value
* @param requiredType
* @return
*/
<B, K, V, T> T getAndSetAsType(B bucket, K key, V value, Class<T> requiredType);
/**
* Variant of {@link org.springframework.data.keyvalue.riak.core.KeyValueStoreOperations#setIfKeyNonExistent(Object,
* Object)} that takes a discreet bucket and key pair.
*
* @param bucket
* @param key
* @param value
* @return
*/
<B, K, V> BucketKeyValueStoreOperations setIfKeyNonExistent(B bucket, K key, V value);
/**
* Variant of {@link org.springframework.data.keyvalue.riak.core.KeyValueStoreOperations#setIfKeyNonExistentAsBytes(Object,
* byte[])} that takes a discreet bucket and key pair.
*
* @param bucket
* @param key
* @param value
* @return
*/
<B, K> BucketKeyValueStoreOperations setIfKeyNonExistentAsBytes(B bucket, K key, byte[] value);
/**
* Variant of {@link org.springframework.data.keyvalue.riak.core.KeyValueStoreOperations#containsKey(Object)}
* that takes a discreet bucket and key pair.
*
* @param bucket
* @param key
* @return
*/
<B, K> boolean containsKey(B bucket, K key);
/**
* Delete a specific entry from this data store.
*
* @param bucket
* @param key
* @return
*/
<B, K> boolean delete(B bucket, K key);
/**
* Variant of {@link org.springframework.data.keyvalue.riak.core.KeyValueStoreOperations#setAsBytes(Object,
* byte[], QosParameters)} that takes a discreet bucket and key pair.
*
* @param bucket
* @param key
* @param value
* @param qosParams
* @return
*/
<B, K> BucketKeyValueStoreOperations setAsBytes(B bucket, K key, byte[] value, QosParameters qosParams);
/**
* Variant of {@link org.springframework.data.keyvalue.riak.core.KeyValueStoreOperations#setWithMetaData(Object,
* Object, java.util.Map)} that takes a discreet bucket and key pair.
*
* @param bucket
* @param key
* @param value
* @param metaData
* @return
*/
<B, K, V> BucketKeyValueStoreOperations setWithMetaData(B bucket, K key, V value, Map<String, String> metaData);
}

View File

@@ -1,52 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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.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 {
String getBucket();
String getKey();
/**
* Get the Content-Type of this object.
*
* @return
*/
MediaType getContentType();
long getLastModified();
/**
* Get the arbitrary properties for this object.
*
* @return
*/
Map<String, Object> getProperties();
}

View File

@@ -1,271 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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 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 operations
/**
* Set a value at a specified key.
*
* @param key
* @param value
* @return This template interface
*/
<K, V> KeyValueStoreOperations set(K key, V value);
/**
* Variation on set() that allows the user to specify {@link org.springframework.data.keyvalue.riak.core.QosParameters}.
*
* @param key
* @param value
* @param qosParams
* @param <K>
* @param <V>
* @return
*/
<K, V> KeyValueStoreOperations set(K key, V value, QosParameters qosParams);
/**
* 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);
/**
* Variation on setWithMetaData() that allows the user to pass {@link
* org.springframework.data.keyvalue.riak.core.QosParameters}.
*
* @param key
* @param value
* @param metaData
* @param qosParams
* @param <K>
* @param <V>
* @return
*/
<K, V> KeyValueStoreOperations setWithMetaData(K key, V value, Map<String, String> metaData, QosParameters qosParams);
// 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 arrays 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);
<B> KeyValueStoreOperations updateBucketSchema(B bucket, Map<String, Object> props);
/**
* 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);
<K> KeyValueStoreOperations setAsBytes(K key, byte[] value, QosParameters qosParams);
<K, V> KeyValueStoreOperations setWithMetaData(K key, V value, Map<String, String> metaData);
}

View File

@@ -1,42 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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;
/**
* 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();
}

View File

@@ -1,52 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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;
/**
* Specify Quality Of Service parameters.
*
* @author J. Brisbin <jon@jbrisbin.com>
*/
public interface QosParameters {
/**
* Instruct the server on the read threshold.
*
* @param <T>
* @return
*/
public <T> T getReadThreshold();
/**
* Instruct the server on the normal write threshold.
*
* @param <T>
* @return
*/
public <T> T getWriteThreshold();
/**
* Instruct the server on the durable write threshold.
*
* @param <T>
* @return
*/
public <T> T getDurableWriteThreshold();
}

View File

@@ -1,349 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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.InitializingBean;
import org.springframework.data.keyvalue.riak.mapreduce.MapReduceJob;
import org.springframework.data.keyvalue.riak.mapreduce.MapReduceOperations;
import org.springframework.data.keyvalue.riak.mapreduce.RiakMapReduceJob;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.util.Assert;
import org.springframework.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;
/**
* An implementation of {@link org.springframework.data.keyvalue.riak.core.KeyValueStoreOperations}
* and {@link org.springframework.data.keyvalue.riak.mapreduce.MapReduceOperations} for the Riak
* data store.
* <p/>
* To use the RiakTemplate, create a singleton in your Spring application-context.xml:
* <pre><code>
* &lt;bean id="riak" class="org.springframework.data.keyvalue.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.data.keyvalue.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.data.keyvalue.riak.convert.KeyValueStoreMetaData} annotation on the
* object you're storing.</li></ul>
*
* @author J. Brisbin <jon@jbrisbin.com>
*/
public class RiakKeyValueTemplate extends AbstractRiakTemplate implements KeyValueStoreOperations, MapReduceOperations, InitializingBean {
protected RiakTemplate riak;
/**
* Take all the defaults.
*/
public RiakKeyValueTemplate() {
super();
riak = new RiakTemplate();
}
/**
* Use the specified {@link org.springframework.http.client.ClientHttpRequestFactory}.
*
* @param requestFactory
*/
public RiakKeyValueTemplate(ClientHttpRequestFactory requestFactory) {
super(requestFactory);
riak = new RiakTemplate(requestFactory);
}
/**
* Use the specified defaultUri and mapReduceUri.
*
* @param defaultUri
* @param mapReduceUri
*/
public RiakKeyValueTemplate(String defaultUri, String mapReduceUri) {
setRestTemplate(new RestTemplate());
this.setDefaultUri(defaultUri);
this.mapReduceUri = mapReduceUri;
this.riak = new RiakTemplate(defaultUri, mapReduceUri);
}
@Override
public void afterPropertiesSet() throws Exception {
super.afterPropertiesSet();
riak.afterPropertiesSet();
}
/*----------------- Set Operations -----------------*/
public <K, V> KeyValueStoreOperations set(K key, V value) {
return setWithMetaData(key, value, null, null);
}
public <K, V> KeyValueStoreOperations set(K key, V value, QosParameters qosParams) {
return setWithMetaData(key, value, null, qosParams);
}
public <K> KeyValueStoreOperations setAsBytes(K key, byte[] value) {
return setAsBytes(key, value, null);
}
public <K> KeyValueStoreOperations setAsBytes(K key, byte[] value, QosParameters qosParams) {
Assert.notNull(key, "Key cannot be null!");
BucketKeyPair bucketKeyPair = resolveBucketKeyPair(key, value);
riak.setAsBytes(bucketKeyPair.getBucket(), bucketKeyPair.getKey(), value, qosParams);
return this;
}
public <K, V> KeyValueStoreOperations setWithMetaData(K key, V value, Map<String, String> metaData, QosParameters qosParams) {
BucketKeyPair bucketKeyPair = resolveBucketKeyPair(key, value);
riak.setWithMetaData(bucketKeyPair.getBucket(),
bucketKeyPair.getKey(),
value,
metaData,
qosParams);
return this;
}
public <K, V> KeyValueStoreOperations setWithMetaData(K key, V value, Map<String, String> metaData) {
BucketKeyPair bucketKeyPair = resolveBucketKeyPair(key, value);
riak.setWithMetaData(bucketKeyPair.getBucket(),
bucketKeyPair.getKey(),
value,
metaData,
null);
return this;
}
/*----------------- Get Operations -----------------*/
public <K> RiakMetaData getMetaData(K key) {
BucketKeyPair bucketKeyPair = resolveBucketKeyPair(key, null);
return riak.getMetaData(bucketKeyPair.getBucket(), bucketKeyPair.getKey());
}
public <K, T> RiakValue<T> getWithMetaData(K key, Class<T> requiredType) {
BucketKeyPair bucketKeyPair = resolveBucketKeyPair(key, null);
return riak.getWithMetaData(bucketKeyPair.getBucket(),
bucketKeyPair.getKey(),
requiredType);
}
@SuppressWarnings({"unchecked"})
public <K, V> V get(K key) {
BucketKeyPair bucketKeyPair = resolveBucketKeyPair(key, null);
return (V) riak.get(bucketKeyPair.getBucket(), bucketKeyPair.getKey());
}
public <K> byte[] getAsBytes(K key) {
BucketKeyPair bucketKeyPair = resolveBucketKeyPair(key, null);
RiakValue<byte[]> obj = riak.getAsBytesWithMetaData(bucketKeyPair.getBucket(),
bucketKeyPair.getKey());
return (null != obj ? obj.get() : null);
}
public <K> RiakValue<byte[]> getAsBytesWithMetaData(K key) {
BucketKeyPair bucketKeyPair = resolveBucketKeyPair(key, null);
return riak.getAsBytesWithMetaData(bucketKeyPair.getBucket(), bucketKeyPair.getKey());
}
public <K, T> T getAsType(K key, Class<T> requiredType) {
BucketKeyPair bucketKeyPair = resolveBucketKeyPair(key, null);
return riak.getAsType(bucketKeyPair.getBucket(), bucketKeyPair.getKey(), requiredType);
}
public <K, V> V getAndSet(K key, V value) {
BucketKeyPair bucketKeyPair = resolveBucketKeyPair(key, null);
return riak.getAndSet(bucketKeyPair.getBucket(), bucketKeyPair.getKey(), value);
}
public <K> byte[] getAndSetAsBytes(K key, byte[] value) {
BucketKeyPair bucketKeyPair = resolveBucketKeyPair(key, null);
return riak.getAndSetAsBytes(bucketKeyPair.getBucket(), bucketKeyPair.getKey(), value);
}
public <K, V, T> T getAndSetAsType(K key, V value, Class<T> requiredType) {
BucketKeyPair bucketKeyPair = resolveBucketKeyPair(key, null);
return riak.getAndSetAsType(bucketKeyPair.getBucket(),
bucketKeyPair.getKey(),
value,
requiredType);
}
@SuppressWarnings({"unchecked"})
public <K, V> List<V> getValues(List<K> keys) {
List<V> results = new ArrayList<V>();
for (K key : keys) {
BucketKeyPair bkp = resolveBucketKeyPair(key, null);
results.add((V) riak.get(bkp.getBucket(), bkp.getKey()));
}
return results;
}
public <K, V> List<V> getValues(K... keys) {
return getValues(keys);
}
public <K, T> List<T> getValuesAsType(List<K> keys, Class<T> requiredType) {
List<T> results = new ArrayList<T>();
for (K key : keys) {
BucketKeyPair bkp = resolveBucketKeyPair(key, null);
results.add(riak.getAsType(bkp.getBucket(), bkp.getKey(), requiredType));
}
return results;
}
public <T, K> List<T> getValuesAsType(Class<T> requiredType, K... keys) {
return riak.getValuesAsType(requiredType, keys);
}
/*----------------- Only-Set-Once Operations -----------------*/
public <K, V> KeyValueStoreOperations setIfKeyNonExistent(K key, V value) {
BucketKeyPair bucketKeyPair = resolveBucketKeyPair(key, null);
riak.setIfKeyNonExistent(bucketKeyPair.getBucket(), bucketKeyPair.getKey(), value);
return this;
}
public <K> KeyValueStoreOperations setIfKeyNonExistentAsBytes(K key, byte[] value) {
BucketKeyPair bucketKeyPair = resolveBucketKeyPair(key, null);
riak.setIfKeyNonExistent(bucketKeyPair.getBucket(), bucketKeyPair.getKey(), value);
return this;
}
/*----------------- Multiple Item Operations -----------------*/
public <K, V> KeyValueStoreOperations setMultiple(Map<K, V> keysAndValues) {
for (Map.Entry<K, V> entry : keysAndValues.entrySet()) {
set(entry.getKey(), entry.getValue());
}
return this;
}
public <K> KeyValueStoreOperations setMultipleAsBytes(Map<K, byte[]> keysAndValues) {
for (Map.Entry<K, byte[]> entry : keysAndValues.entrySet()) {
setAsBytes(entry.getKey(), entry.getValue());
}
return this;
}
public <K, V> KeyValueStoreOperations setMultipleIfKeysNonExistent(Map<K, V> keysAndValues) {
for (Map.Entry<K, V> entry : keysAndValues.entrySet()) {
setIfKeyNonExistent(entry.getKey(), entry.getValue());
}
return this;
}
public <K> KeyValueStoreOperations setMultipleAsBytesIfKeysNonExistent(Map<K, byte[]> keysAndValues) {
for (Map.Entry<K, byte[]> entry : keysAndValues.entrySet()) {
setIfKeyNonExistentAsBytes(entry.getKey(), entry.getValue());
}
return this;
}
/*----------------- Key Operations -----------------*/
public <K> boolean containsKey(K key) {
BucketKeyPair bucketKeyPair = resolveBucketKeyPair(key, null);
return riak.containsKey(bucketKeyPair.getBucket(), bucketKeyPair.getKey());
}
public <K> boolean deleteKeys(K... keys) {
return riak.deleteKeys(keys);
}
/*----------------- Map/Reduce Operations -----------------*/
public RiakMapReduceJob createMapReduceJob() {
return new RiakMapReduceJob(riak);
}
public Object execute(MapReduceJob job) {
return execute(job, List.class);
}
public <T> T execute(MapReduceJob job, Class<T> targetType) {
return riak.execute(job, targetType);
}
public <T> Future<List<T>> submit(MapReduceJob job) {
// Run this job asynchronously.
return riak.submit(job);
}
/*----------------- 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> RiakKeyValueTemplate link(K1 destination, K2 source, String tag) {
BucketKeyPair bkpFrom = resolveBucketKeyPair(source, null);
BucketKeyPair bkpTo = resolveBucketKeyPair(destination, null);
riak.link(bkpTo.getBucket(), bkpTo.getKey(), bkpFrom.getBucket(), bkpFrom.getKey(), tag);
return this;
}
/**
* 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).
*
* @param source
* @param tag
* @return
*/
@SuppressWarnings({"unchecked"})
public <T, K> T linkWalk(K source, String tag) {
BucketKeyPair bkpSource = resolveBucketKeyPair(source, null);
return (T) riak.linkWalk(bkpSource.getBucket(), bkpSource.getKey(), tag);
}
/*----------------- Bucket Operations -----------------*/
public <B> Map<String, Object> getBucketSchema(B bucket) {
return riak.getBucketSchema(bucket, false);
}
public <B> Map<String, Object> getBucketSchema(B bucket, boolean listKeys) {
return riak.getBucketSchema(bucket, listKeys);
}
public <B> KeyValueStoreOperations updateBucketSchema(B bucket, Map<String, Object> props) {
riak.updateBucketSchema(bucket, props);
return this;
}
}

View File

@@ -1,83 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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.http.MediaType;
import java.util.Date;
import java.util.Map;
/**
* An implementation of {@link org.springframework.data.keyvalue.riak.core.KeyValueStoreMetaData}
* for Riak.
*
* @author J. Brisbin <jon@jbrisbin.com>
*/
public class RiakMetaData implements KeyValueStoreMetaData {
private MediaType mediaType = MediaType.APPLICATION_JSON;
private Map<String, Object> properties;
private String bucket = null;
private String key = null;
public RiakMetaData(Map<String, Object> properties) {
this.properties = properties;
}
public RiakMetaData(MediaType mediaType, Map<String, Object> properties) {
this.mediaType = mediaType;
this.properties = properties;
}
public RiakMetaData(MediaType mediaType, Map<String, Object> properties, String bucket, String key) {
this.mediaType = mediaType;
this.properties = properties;
this.bucket = bucket;
this.key = key;
}
public void setBucket(String bucket) {
this.bucket = bucket;
}
public void setKey(String key) {
this.key = key;
}
public String getBucket() {
return this.bucket;
}
public String getKey() {
return this.key;
}
public MediaType getContentType() {
return mediaType;
}
public long getLastModified() {
return ((Date) properties.get("Last-Modified")).getTime();
}
public Map<String, Object> getProperties() {
return this.properties;
}
}

View File

@@ -1,57 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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;
/**
* A generic class for specifying Quality Of Service parameters on operations.
*
* @author J. Brisbin <jon@jbrisbin.com>
*/
@SuppressWarnings({"unchecked"})
public class RiakQosParameters implements QosParameters {
public Object readThreshold = null;
public Object writeThreshold = null;
public Object durableWriteThreshold = null;
public <T> void setReadThreshold(T readThreshold) {
this.readThreshold = readThreshold;
}
public <T> void setWriteThreshold(T writeThreshold) {
this.writeThreshold = writeThreshold;
}
public <T> void setDurableWriteThreshold(T durableWriteThreshold) {
this.durableWriteThreshold = durableWriteThreshold;
}
public <T> T getReadThreshold() {
return (T) this.readThreshold;
}
public <T> T getWriteThreshold() {
return (T) this.writeThreshold;
}
public <T> T getDurableWriteThreshold() {
return (T) this.durableWriteThreshold;
}
}

View File

@@ -1,781 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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.core.convert.ConversionService;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.data.keyvalue.riak.DataStoreOperationException;
import org.springframework.data.keyvalue.riak.mapreduce.MapReduceJob;
import org.springframework.data.keyvalue.riak.mapreduce.MapReduceOperations;
import org.springframework.http.*;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.client.*;
import javax.mail.BodyPart;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMultipart;
import javax.mail.util.ByteArrayDataSource;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.*;
import java.util.concurrent.Future;
/**
* An implementation of {@link org.springframework.data.keyvalue.riak.core.BucketKeyValueStoreOperations}
* and {@link org.springframework.data.keyvalue.riak.mapreduce.MapReduceOperations} for the Riak
* data store.
* <p/>
* To use the RiakTemplate, create a singleton in your Spring application-context.xml:
* <pre><code>
* &lt;bean id="riak" class="org.springframework.data.keyvalue.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("mybucket", "mykey", obj)
* ...
* def name = riak.get("mybucket", "mykey").name
* println "Hello $name!"
* </code></pre>
*
* @author J. Brisbin <jon@jbrisbin.com>
*/
public class RiakTemplate extends AbstractRiakTemplate implements BucketKeyValueStoreOperations, MapReduceOperations {
/**
* Take all the defaults.
*/
public RiakTemplate() {
super();
}
/**
* Use the specified {@link org.springframework.http.client.ClientHttpRequestFactory}.
*
* @param requestFactory
*/
public RiakTemplate(ClientHttpRequestFactory requestFactory) {
super(requestFactory);
}
/**
* Use the specified defaultUri and mapReduceUri.
*
* @param defaultUri
* @param mapReduceUri
*/
public RiakTemplate(String defaultUri, String mapReduceUri) {
setRestTemplate(new RestTemplate());
this.setDefaultUri(defaultUri);
this.mapReduceUri = mapReduceUri;
}
/*----------------- Set Operations -----------------*/
public <B, K, V> BucketKeyValueStoreOperations set(B bucket, K key, V value) {
return setWithMetaData(bucket, key, value, null, null);
}
public <B, K, V> BucketKeyValueStoreOperations set(B bucket, K key, V value,
QosParameters qosParams) {
return setWithMetaData(bucket, key, value, null, qosParams);
}
public <B, K> BucketKeyValueStoreOperations setAsBytes(B bucket, K key, byte[] value) {
return setAsBytes(bucket, key, value, null);
}
public <B, K> BucketKeyValueStoreOperations setAsBytes(B bucket, K key, byte[] value,
QosParameters qosParams) {
return setWithMetaData(bucket, key, value, null, qosParams);
}
public <B, K, V> BucketKeyValueStoreOperations setWithMetaData(B bucket, K key, V value,
Map<String, String> metaData,
QosParameters qosParams) {
Assert.notNull(key, "Key cannot be null!");
// Get a key name that may or may not include the QOS parameters.
String keyName = (null != qosParams ? key.toString() + extractQosParameters(qosParams) : key
.toString());
KeyValueStoreMetaData origMeta = getMetaData(bucket, keyName);
String vclock = null;
if (null != origMeta) {
Map<String, Object> mprops = origMeta.getProperties();
if (null != mprops) {
Object o = mprops.get(RIAK_VCLOCK);
if (null != o) {
vclock = o.toString();
}
}
}
RestTemplate restTemplate = getRestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.set("X-Riak-ClientId", RIAK_CLIENT_ID);
if (log.isDebugEnabled() && null == value) {
log.debug("bucket=" + bucket + ", key=" + key);
}
headers.setContentType(extractMediaType(value));
if (null != vclock) {
headers.set(RIAK_VCLOCK, vclock);
}
if (null != metaData) {
for (Map.Entry<String, String> entry : metaData.entrySet()) {
headers.set(entry.getKey(), entry.getValue());
}
}
headers.set(RIAK_META_CLASSNAME,
(null != value ? value.getClass().getName() : defaultType.getName()));
HttpEntity<V> entity = new HttpEntity<V>(value, headers);
try {
restTemplate.put(defaultUri, entity, bucket, keyName);
if (log.isDebugEnabled()) {
log.debug(String.format("PUT object: bucket=%s, key=%s, value=%s", bucket, key, value));
}
} catch (RestClientException e) {
throw new DataStoreOperationException(e.getMessage(), e);
}
return this;
}
public <B, K, V> BucketKeyValueStoreOperations setWithMetaData(B bucket, K key, V value,
Map<String, String> metaData) {
return setWithMetaData(bucket, key, value, metaData, null);
}
/*----------------- Put Operations -----------------*/
/**
* Save an object to Riak and let it generate an ID for it.
*
* @param bucket
* @param value
* @return The generated ID
*/
public <B, V> String put(B bucket, V value) {
return put(bucket, value, null);
}
/**
* Save an object to Riak and let it generate an ID for it.
*
* @param bucket
* @param value
* @param metaData
* @return The generated ID
*/
public <B, V> String put(B bucket, V value, Map<String, String> metaData) {
Assert.notNull(bucket, "Bucket cannot be null.");
String bucketName = bucket.toString();
RestTemplate restTemplate = getRestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.set("X-Riak-ClientId", RIAK_CLIENT_ID);
headers.setContentType(extractMediaType(value));
if (null != metaData) {
for (Map.Entry<String, String> entry : metaData.entrySet()) {
headers.set(entry.getKey(), entry.getValue());
}
}
headers.set(RIAK_META_CLASSNAME, value.getClass().getName());
HttpEntity<V> entity = new HttpEntity<V>(value, headers);
try {
URI uri = restTemplate.postForLocation(defaultUri, entity, bucketName, "");
String suri = uri.toString();
String id = suri.substring(suri.lastIndexOf("/") + 1);
if (log.isDebugEnabled()) {
log.debug("New ID: " + id);
}
return id;
} catch (RestClientException e) {
throw new DataStoreOperationException(e.getMessage(), e);
}
}
/*----------------- Get Operations -----------------*/
public <B, K> RiakMetaData getMetaData(B bucket, K key) {
RestTemplate restTemplate = getRestTemplate();
HttpHeaders headers;
try {
headers = restTemplate.headForHeaders(defaultUri, bucket, key);
RiakMetaData meta = extractMetaData(headers);
meta.setBucket((null != bucket ? bucket.toString() : null));
meta.setKey((null != key ? key.toString() : null));
return meta;
} catch (ResourceAccessException e) {
} catch (IOException e) {
throw new DataAccessResourceFailureException(e.getMessage(), e);
}
return null;
}
@SuppressWarnings({"unchecked"})
public <B, K, T> RiakValue<T> getWithMetaData(B bucket, K key, Class<T> requiredType) {
// If no bucket name is given, infer it from the type name.
String bucketName = (null != bucket ? bucket.toString() : requiredType.getName());
RestTemplate restTemplate = getRestTemplate();
if (log.isDebugEnabled()) {
log.debug(String.format("GET object: bucket=%s, key=%s, type=%s",
bucketName,
key,
requiredType.getName()));
}
Class<?> origType = getType(bucket, key, classLoader);
RiakValue<T> val = null;
try {
ResponseEntity<?> result = restTemplate.getForEntity(defaultUri,
requiredType,
bucketName,
key);
val = extractValue(result, requiredType, requiredType);
} catch (HttpClientErrorException e) {
switch (e.getStatusCode()) {
case NOT_ACCEPTABLE:
// Can't convert using HttpMessageConverter. Try fetching as the original type
// and using the conversion service to convert.
ResponseEntity<?> result = restTemplate.getForEntity(defaultUri,
origType,
bucketName,
key);
try {
val = extractValue(result, origType, requiredType);
} catch (IOException ioe) {
throw new DataStoreOperationException(ioe.getMessage(), ioe);
}
break;
case NOT_FOUND:
// IGNORED
break;
default:
throw new DataStoreOperationException(e.getMessage(), e);
}
if (e.getStatusCode() != HttpStatus.NOT_FOUND) {
throw new DataStoreOperationException(e.getMessage(), e);
}
} catch (RestClientException rce) {
if (rce.getMessage().contains("HTTP response code: 406")) {
// Can't convert using HttpMessageConverter. Try fetching as the original type
// and using the conversion service to convert.
ResponseEntity<?> result = restTemplate.getForEntity(defaultUri,
origType,
bucketName,
key);
try {
val = extractValue(result, origType, requiredType);
} catch (IOException ioe) {
throw new DataStoreOperationException(rce.getMessage(), rce);
}
} else {
// IGNORE
if (log.isDebugEnabled()) {
log.debug("RestClientException: " + rce.getMessage());
}
}
} catch (EOFException eof) {
// IGNORE
if (log.isDebugEnabled()) {
log.debug("EOFException: " + eof.getMessage(), eof);
}
} catch (IOException e) {
log.error(e.getMessage(), e);
}
if (null != val && useCache) {
cache.put(new SimpleBucketKeyPair<Object, Object>(bucket, key), val);
}
return val;
}
@SuppressWarnings({"unchecked"})
public <B, K, T> T get(B bucket, K key) {
Class targetClass = getType(bucket, key, classLoader);
RiakValue<T> obj = getWithMetaData(bucket, key, targetClass);
return (null != obj ? obj.get() : null);
}
public <B, K> byte[] getAsBytes(B bucket, K key) {
RiakValue<byte[]> obj = getAsBytesWithMetaData(bucket, key);
return (null != obj ? obj.get() : null);
}
@SuppressWarnings({"unchecked"})
public <B, K> RiakValue<byte[]> getAsBytesWithMetaData(final B bucket, final K key) {
final RestTemplate restTemplate = getRestTemplate();
if (log.isDebugEnabled()) {
log.debug(String.format("GET object: bucket=%s, key=%s, type=byte[]",
bucket,
key));
}
try {
RiakValue<byte[]> bytes = (RiakValue<byte[]>) restTemplate.execute(
defaultUri,
HttpMethod.GET,
new RequestCallback() {
public void doWithRequest(ClientHttpRequest request) throws
IOException {
List<MediaType> mediaTypes = new ArrayList<MediaType>();
mediaTypes.add(MediaType.APPLICATION_JSON);
mediaTypes.add(MediaType.APPLICATION_OCTET_STREAM);
request.getHeaders().setAccept(mediaTypes);
}
},
new ResponseExtractor<Object>() {
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)) {
out.write(buff, 0, bytesRead);
}
HttpHeaders headers = response.getHeaders();
RiakMetaData meta = extractMetaData(headers);
meta.setBucket((null != bucket ? bucket.toString() : null));
meta.setKey((null != key ? key.toString() : null));
RiakValue<byte[]> val = new RiakValue<byte[]>(out.toByteArray(),
meta);
return val;
}
},
bucket,
key);
if (useCache) {
cache.put(new SimpleBucketKeyPair(bucket, key), bytes);
}
return bytes;
} catch (HttpClientErrorException e) {
if (e.getStatusCode() != HttpStatus.NOT_FOUND) {
throw new DataStoreOperationException(e.getMessage(), e);
}
} catch (RestClientException e) {
throw new DataStoreOperationException(e.getMessage(), e);
}
return null;
}
@SuppressWarnings({"unchecked"})
public <B, K, T> T getAsType(B bucket, K key, Class<T> requiredType) {
if (useCache) {
Object obj = checkCache(new SimpleBucketKeyPair(bucket, key), requiredType);
if (null != obj) {
return (T) obj;
}
}
RiakValue<T> obj = getWithMetaData(bucket, key, requiredType);
return (null != obj ? obj.get() : null);
}
@SuppressWarnings({"unchecked"})
public <B, K, V> V getAndSet(B bucket, K key, V value) {
V old = (V) getAsType(bucket, key, value.getClass());
set(bucket, key, value, null);
return old;
}
public <B, K> byte[] getAndSetAsBytes(B bucket, K key, byte[] value) {
byte[] old = getAsBytes(bucket, key);
setAsBytes(bucket, key, value);
return old;
}
public <B, K, V, T> T getAndSetAsType(B bucket, K key, V value, Class<T> requiredType) {
T old = getAsType(bucket, key, requiredType);
set(bucket, key, value);
return old;
}
@SuppressWarnings({"unchecked"})
public <K, V> List<V> getValues(List<K> keys) {
List<V> results = new ArrayList<V>();
for (K key : keys) {
BucketKeyPair bkp = resolveBucketKeyPair(key, null);
results.add((V) get(bkp.getBucket(), bkp.getKey()));
}
return results;
}
public <K, V> List<V> getValues(K... keys) {
return getValues(keys);
}
public <K, T> List<T> getValuesAsType(List<K> keys, Class<T> requiredType) {
List<T> results = new ArrayList<T>();
for (K key : keys) {
BucketKeyPair bkp = resolveBucketKeyPair(key, null);
results.add(getAsType(bkp.getBucket(), bkp.getKey(), requiredType));
}
return results;
}
public <T, K> List<T> getValuesAsType(Class<T> requiredType, K... keys) {
List<K> keyList = new ArrayList<K>(keys.length);
return getValuesAsType(keyList, requiredType);
}
/*----------------- Only-Set-Once Operations -----------------*/
public <B, K, V> BucketKeyValueStoreOperations setIfKeyNonExistent(B bucket, K key, V value) {
if (!containsKey(bucket, key)) {
set(bucket, key, value);
} else {
if (log.isDebugEnabled()) {
log.debug(String.format("key: %s already exists. Not adding %s",
key,
value));
}
}
return this;
}
public <B, K> BucketKeyValueStoreOperations setIfKeyNonExistentAsBytes(B bucket, K key,
byte[] value) {
if (!containsKey(bucket, key)) {
setAsBytes(bucket, key, value);
} else {
if (log.isDebugEnabled()) {
log.debug(String.format("key: %s already exists. Not adding %s",
key,
value));
}
}
return this;
}
/*----------------- Key Operations -----------------*/
public <B, K> boolean containsKey(B bucket, K key) {
RestTemplate restTemplate = getRestTemplate();
HttpHeaders headers = null;
try {
headers = restTemplate.headForHeaders(defaultUri, bucket, key);
} catch (ResourceAccessException e) {
}
return (null != headers);
}
@SuppressWarnings({"unchecked"})
public <B, K> boolean delete(B bucket, K key) {
return deleteKeys(new SimpleBucketKeyPair(bucket, key));
}
public <K> boolean deleteKeys(K... keys) {
boolean stillExists = false;
RestTemplate restTemplate = getRestTemplate();
for (K key : keys) {
BucketKeyPair bkp = resolveBucketKeyPair(key, null);
try {
restTemplate.delete(defaultUri, bkp.getBucket(), bkp.getKey());
} catch (HttpClientErrorException e) {
if (e.getStatusCode() != HttpStatus.NOT_FOUND) {
throw new DataAccessResourceFailureException(e.getMessage(), e);
}
}
//if (!stillExists) {
//stillExists = containsKey(key);
//}
}
return !stillExists;
}
/*----------------- Map/Reduce Operations -----------------*/
public Object execute(MapReduceJob job) {
return execute(job, List.class);
}
@SuppressWarnings({"unchecked"})
public <T> T execute(MapReduceJob job, Class<T> targetType) {
RestTemplate restTemplate = getRestTemplate();
try {
ResponseEntity<List> resp = restTemplate.postForEntity(mapReduceUri,
job.toJson(),
List.class);
if (resp.hasBody()) {
if (!targetType.isAssignableFrom(List.class)) {
// M/R jobs always return a List. Try to turn the List into something else.
List<?> results = (List<?>) resp.getBody();
if (results.size() == 1) {
// A List of size 1 get's returned as the object at list[0].
Object obj = results.get(0);
if (obj.getClass() != targetType) {
// I can't just return it as-is, I have to convert it first.
ConversionService conv = getConversionService();
if (conv.canConvert(obj.getClass(), targetType)) {
return conv.convert(obj, targetType);
} else {
throw new DataAccessResourceFailureException(
"Can't find a converter to to convert " + obj
.getClass() + " returned from M/R job to required type " + targetType);
}
} else {
return (T) obj;
}
}
}
return (T) resp.getBody();
}
} catch (RestClientException e) {
throw new DataStoreOperationException(e.getMessage(), e);
}
return null;
}
@SuppressWarnings({"unchecked"})
public <T> Future<List<T>> submit(MapReduceJob job) {
// Run this job asynchronously.
return workerPool.submit(job);
}
/*----------------- Link Operations -----------------*/
/**
* Use Riak's native Link mechanism to link two entries together.
*
* @param destBucket Bucket of child entry
* @param destKey Key of child entry
* @param sourceBucket Bucket of parent entry
* @param sourceKey Key of parent entry
* @param tag Tag for this relationship
* @return
*/
@SuppressWarnings({"unchecked"})
public <B1, K1, B2, K2> RiakTemplate link(B1 destBucket, K1 destKey, B2 sourceBucket,
K2 sourceKey, String tag) {
RestTemplate restTemplate = getRestTemplate();
// Skip all conversion on the data since all we care about is the Link header.
RiakValue<byte[]> fromObj = getAsBytesWithMetaData(sourceBucket, sourceKey);
if (null == fromObj) {
throw new DataStoreOperationException(
"Cannot link from a non-existent source: " + sourceBucket + ":" + sourceKey);
}
HttpHeaders headers = new HttpHeaders();
headers.setContentType(fromObj.getMetaData().getContentType());
headers.set(RIAK_VCLOCK, fromObj.getMetaData().getProperties().get(RIAK_VCLOCK).toString());
Object linksObj = fromObj.getMetaData().getProperties().get("Link");
List<String> links = new ArrayList<String>();
// First add all existing links...
if (linksObj instanceof List) {
links.addAll((List) linksObj);
} else if (linksObj instanceof String) {
links.add(linksObj.toString());
}
// ...then add the link we're creating...
links.add(String.format("<%s/%s/%s>; riaktag=\"%s\"",
getPrefix(),
destBucket,
destKey,
tag));
String linkHeader = StringUtils.collectionToCommaDelimitedString(links);
headers.set("Link", linkHeader);
// Make sure to store the data back, otherwise it gets lost!
// Basho at some point will likely add the ability to updated metadata separate
// from the content. Until then, we have to transfer the body back-and-forth.
HttpEntity entity = new HttpEntity(fromObj.get(), headers);
restTemplate.put(defaultUri, entity, sourceBucket, sourceKey);
return this;
}
/**
* 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).
*
* @param bucket
* @param key
* @param tag
* @return
*/
@SuppressWarnings({"unchecked"})
public <B, T, K> 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: <ol> <li>The type
* specified by <code>requiredType</code></li> <li>If that's null, try using the bucket name
* in which the object was stored</li> <li>If all else fails, use a {@link java.util.Map}</li>
* </ol>
*
* @param bucket
* @param key
* @param tag
* @param requiredType
* @return
*/
@SuppressWarnings({"unchecked"})
public <B, T, K> T linkWalkAsType(B bucket, K key, String tag, final Class<T> requiredType) {
final RestTemplate restTemplate = getRestTemplate();
final List<MediaType> types = new ArrayList<MediaType>();
types.add(MediaType.ALL);
T returnObj = (T) restTemplate.execute(defaultUri + "/_,{tag},_",
HttpMethod.GET,
new RequestCallback() {
public void doWithRequest(ClientHttpRequest request) throws
IOException {
// Make sure I can accept a multipart/mixed response.
request.getHeaders().setAccept(types);
}
},
new ResponseExtractor<Object>() {
@SuppressWarnings({"unchecked"})
public Object extractData(ClientHttpResponse response) throws
IOException {
String contentType = ((List) response.getHeaders().get("Content-Type")).get(0)
.toString();
if (contentType.startsWith("multipart/mixed")) {
List<Object> results = new LinkedList<Object>();
ByteArrayDataSource ds = new ByteArrayDataSource(response.getBody(),
"multipart/mixed");
try {
// All this mess is for extracting multipart data from our response.
// I'm using the javax.mail stuff because it's the best multipart library
// that's in Maven and it's a common dependency anyway.
MimeMultipart mp = new MimeMultipart(ds);
int msgCnt = mp.getCount();
for (int i = 0; i < msgCnt; i++) {
BodyPart bp = mp.getBodyPart(i);
if (bp.getContentType().startsWith("multipart/mixed")) {
MimeMultipart part = (MimeMultipart) bp.getContent();
int partCnt = part.getCount();
for (int j = 0; j < partCnt; j++) {
final BodyPart partBody = part.getBodyPart(j);
String partType = partBody.getContentType();
String link = partBody.getHeader("Link")[0];
String location = partBody.getHeader("Location")[0];
String key = location.substring(location.lastIndexOf("/") + 1);
String[] links = StringUtils.delimitedListToStringArray(link, ",");
String bucketName = null;
for (String s : links) {
if (s.contains("rel=\"up\"")) {
String[] linkParts = StringUtils.delimitedListToStringArray(s, ";");
int start = linkParts[0].lastIndexOf("/");
bucketName = linkParts[0].substring(start + 1,
linkParts[0].length() - 1);
break;
}
}
Class<?> clazz = requiredType;
if (null == clazz) {
clazz = getType(bucketName, key, classLoader);
}
// Can convert message?
for (HttpMessageConverter converter : restTemplate
.getMessageConverters()) {
if (converter.canRead(clazz, MediaType.parseMediaType(partType))) {
HttpInputMessage msg = new HttpInputMessage() {
public InputStream getBody() throws IOException {
try {
return partBody.getInputStream();
} catch (MessagingException e) {
log.error(e.getMessage(), e);
}
return null;
}
public HttpHeaders getHeaders() {
return new HttpHeaders();
}
};
results.add(converter.read(clazz, msg));
break;
}
}
if (log.isDebugEnabled()) {
log.debug(String.format("results=%s", results));
}
}
}
}
} catch (MessagingException e) {
log.error(e.getMessage(), e);
}
return results;
}
return null;
}
},
bucket,
key,
tag);
return returnObj;
}
/*----------------- Bucket Operations -----------------*/
public <B> Map<String, Object> getBucketSchema(B bucket) {
return getBucketSchema(bucket, false);
}
@SuppressWarnings({"unchecked"})
public <B> Map<String, Object> getBucketSchema(B bucket, boolean listKeys) {
RestTemplate restTemplate = getRestTemplate();
ResponseEntity<Map> resp = restTemplate.getForEntity(defaultUri,
Map.class,
bucket,
(listKeys ? "?keys=true" : ""));
if (resp.hasBody()) {
return resp.getBody();
} else {
throw new DataStoreOperationException(
"Error encountered retrieving bucket schema (Status: " + resp.getStatusCode() + ")");
}
}
@SuppressWarnings({"unchecked"})
public <B> BucketKeyValueStoreOperations updateBucketSchema(B bucket,
Map<String, Object> props) {
Map<Object, Object> bucketProps = new LinkedHashMap<Object, Object>();
bucketProps.put("props", props);
RestTemplate restTemplate = getRestTemplate();
String bucketName;
if (bucket instanceof String) {
bucketName = bucket.toString();
} else {
BucketKeyPair bkp = resolveBucketKeyPair(bucket, null);
bucketName = bkp.getBucket().toString();
}
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity entity = new HttpEntity(bucketProps, headers);
restTemplate.put(defaultUri, entity, bucketName, "");
return this;
}
}

View File

@@ -1,42 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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;
/**
* @author J. Brisbin <jon@jbrisbin.com>
*/
@SuppressWarnings({"unchecked"})
public class RiakValue<T> implements KeyValueStoreValue {
private Object delegate;
private KeyValueStoreMetaData metaData;
public RiakValue(T delegate, KeyValueStoreMetaData metaData) {
this.delegate = delegate;
this.metaData = metaData;
}
public KeyValueStoreMetaData getMetaData() {
return this.metaData;
}
public T get() {
return (T) delegate;
}
}

View File

@@ -1,57 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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;
/**
* @author J. Brisbin <jon@jbrisbin.com>
*/
@SuppressWarnings({"unchecked"})
public class SimpleBucketKeyPair<B, K> implements BucketKeyPair, Comparable {
private Object bucket;
private Object key;
public SimpleBucketKeyPair(B bucket, K key) {
this.bucket = bucket;
this.key = key;
}
public B getBucket() {
return (B) bucket;
}
public K getKey() {
return (K) key;
}
public int compareTo(Object o) {
if (o instanceof SimpleBucketKeyPair) {
SimpleBucketKeyPair pair = (SimpleBucketKeyPair) o;
if (pair.getBucket().equals(bucket) && pair.getKey().equals(key)) {
return 0;
}
}
return -1;
}
@Override
public String toString() {
return String.format("{bucket=%s, key=%s}", bucket, key);
}
}

View File

@@ -1,76 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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.codehaus.groovy.runtime.GStringImpl;
import org.springframework.util.ClassUtils;
import java.util.Map;
import java.util.regex.Pattern;
/**
* @author J. Brisbin <jon@jbrisbin.com>
*/
@SuppressWarnings({"unchecked"})
public class SimpleBucketKeyResolver implements BucketKeyResolver {
private final boolean groovyPresent = ClassUtils.isPresent(
"org.codehaus.groovy.runtime.GStringImpl",
getClass().getClassLoader());
protected Pattern bucketColonKey = Pattern.compile("(.+):(.+)");
public <V> boolean canResolve(V o) {
if (o instanceof String) {
return true;
} else if (o instanceof Map) {
Map m = (Map) o;
return (m.containsKey("bucket") && m.containsKey("key"));
} else if (o instanceof BucketKeyPair) {
return true;
} else if (groovyPresent && o instanceof GStringImpl) {
return true;
}
return false;
}
public <B, K, V> BucketKeyPair<B, K> resolve(V o) {
BucketKeyPair<B, K> bucketKeyPair = null;
if (o instanceof String) {
String[] s = ((String) o).split(":");
bucketKeyPair = new SimpleBucketKeyPair<String, String>((s.length == 1 ? null : s[0]),
(s.length == 1 ? s[0] : s[1]));
} else if (o instanceof Map) {
Map m = (Map) o;
Object bucket = m.get("bucket");
Object key = m.get("key");
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(o.toString());
}
return bucketKeyPair;
}
}

View File

@@ -1,392 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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.io;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.keyvalue.riak.DataStoreOperationException;
import org.springframework.data.keyvalue.riak.core.KeyValueStoreMetaData;
import org.springframework.data.keyvalue.riak.core.RiakTemplate;
import org.springframework.data.keyvalue.riak.core.RiakValue;
import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* @author J. Brisbin <jon@jbrisbin.com>
*/
public class RiakFile<B, K> extends File {
private static final long serialVersionUID = 1L;
protected final Log log = LogFactory.getLog(getClass());
private RiakTemplate riak;
private B bucket;
private K key;
public RiakFile(RiakTemplate riak, B bucket, K key) throws URISyntaxException {
super(riak.getDefaultUri());
this.riak = riak;
this.bucket = bucket;
this.key = key;
}
public String getUriAsString(boolean includeKey) {
String protocol = riak.getDefaultUri().substring(0, riak.getDefaultUri().indexOf(":"));
String uri = String.format("%s://%s:%s%s/%s/%s",
protocol,
riak.getHost(),
riak.getPort(),
riak.getPrefix(),
bucket,
(includeKey ? key : ""));
return uri;
}
public RiakTemplate getRiak() {
return riak;
}
public void setRiak(RiakTemplate riak) {
this.riak = riak;
}
public B getBucket() {
return bucket;
}
public void setBucket(B bucket) {
this.bucket = bucket;
}
public K getKey() {
return key;
}
public void setKey(K key) {
this.key = key;
}
@Override
public String getName() {
return getUriAsString(true);
}
@Override
public String getParent() {
return getUriAsString(false);
}
@SuppressWarnings({"unchecked"})
@Override
public File getParentFile() {
try {
return new RiakFile(riak, bucket, "");
} catch (URISyntaxException e) {
log.error(e.getMessage(), e);
}
return null;
}
@Override
public String getPath() {
return getUriAsString(true);
}
@Override
public boolean isAbsolute() {
return true;
}
@Override
public String getAbsolutePath() {
return getUriAsString(true);
}
@Override
public File getAbsoluteFile() {
return this;
}
@Override
public String getCanonicalPath() throws IOException {
return getUriAsString(true);
}
@Override
public File getCanonicalFile() throws IOException {
return this;
}
@SuppressWarnings({"deprecation"})
@Override
public URL toURL() throws MalformedURLException {
return new URL(getUriAsString(true));
}
@Override
public URI toURI() {
try {
return new URI(getUriAsString(true));
} catch (URISyntaxException e) {
log.error(e.getMessage(), e);
}
return null;
}
@Override
public boolean canRead() {
return true;
}
@Override
public boolean canWrite() {
return true;
}
@Override
public boolean exists() {
return riak.containsKey(bucket, key);
}
@Override
public boolean isDirectory() {
return (key != null && "".equals(key));
}
@Override
public boolean isFile() {
return (key != null && !("".equals(key)));
}
@Override
public boolean isHidden() {
return false;
}
@Override
public long lastModified() {
KeyValueStoreMetaData meta = riak.getMetaData(bucket, key);
return (null != meta ? meta.getLastModified() : null);
}
@Override
public long length() {
return riak.getAsBytes(bucket, key).length;
}
@Override
public boolean createNewFile() throws IOException {
return true;
}
@Override
public boolean delete() {
return riak.delete(bucket, key);
}
@Override
public void deleteOnExit() {
// NO-OP
}
@Override
public String[] list() {
if (isDirectory()) {
Map<String, Object> schema = riak.getBucketSchema(bucket, true);
String baseUri = getUriAsString(false);
List<String> uris = new LinkedList<String>();
for (Object key : (List) ((Map) schema.get("props")).get("keys")) {
uris.add(baseUri + key);
}
return (String[]) uris.toArray();
}
return new String[]{};
}
@Override
public String[] list(FilenameFilter filenameFilter) {
List<String> uris = new LinkedList<String>();
for (String s : list()) {
if (filenameFilter.accept(this, s)) {
uris.add(s);
}
}
return (String[]) uris.toArray();
}
@SuppressWarnings({"unchecked"})
@Override
public File[] listFiles() {
List<RiakFile> uris = new LinkedList<RiakFile>();
for (String s : list()) {
try {
uris.add(new RiakFile(riak, bucket, s.substring(s.lastIndexOf("/"))));
} catch (URISyntaxException e) {
log.error(e.getMessage(), e);
}
}
return (File[]) uris.toArray();
}
@SuppressWarnings({"unchecked"})
@Override
public File[] listFiles(FilenameFilter filenameFilter) {
List<RiakFile> uris = new LinkedList<RiakFile>();
for (String s : list(filenameFilter)) {
try {
uris.add(new RiakFile(riak, bucket, s.substring(s.lastIndexOf("/"))));
} catch (URISyntaxException e) {
log.error(e.getMessage(), e);
}
}
return (File[]) uris.toArray();
}
@Override
public File[] listFiles(FileFilter fileFilter) {
return super.listFiles(fileFilter); //To change body of overridden methods use File | Settings | File Templates.
}
@Override
public boolean mkdir() {
return true;
}
@Override
public boolean mkdirs() {
return true;
}
@SuppressWarnings({"unchecked"})
@Override
public boolean renameTo(File file) {
if (file instanceof RiakFile) {
RiakFile f = (RiakFile) file;
RiakValue<byte[]> v = riak.getAsBytesWithMetaData(bucket, key);
try {
riak.setWithMetaData(f.getBucket(), f.getKey(), v.get(),
(Map<String, String>) v.getMetaData());
} catch (DataStoreOperationException e) {
log.error(e.getMessage(), e);
return false;
}
} else {
throw new IllegalArgumentException("Renaming to a non-Riak file is not yet supported.");
}
return true;
}
@Override
public boolean setLastModified(long l) {
return false;
}
@Override
public boolean setReadOnly() {
return false;
}
@Override
public boolean setWritable(boolean b, boolean b1) {
return true;
}
@Override
public boolean setWritable(boolean b) {
return true;
}
@Override
public boolean setReadable(boolean b, boolean b1) {
return true;
}
@Override
public boolean setReadable(boolean b) {
return true;
}
@Override
public boolean setExecutable(boolean b, boolean b1) {
return false;
}
@Override
public boolean setExecutable(boolean b) {
return false;
}
@Override
public boolean canExecute() {
return false;
}
@Override
public long getTotalSpace() {
return super.getTotalSpace();
}
@Override
public long getFreeSpace() {
return super.getFreeSpace();
}
@Override
public long getUsableSpace() {
return super.getUsableSpace();
}
@Override
public int compareTo(File file) {
if (file instanceof RiakFile) {
return 0;
} else {
return -1;
}
}
@Override
public boolean equals(Object o) {
if (o instanceof RiakFile) {
RiakFile rf = (RiakFile) o;
return (rf.getBucket().equals(bucket) && rf.getKey().equals(key));
}
return false;
}
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public String toString() {
return getClass().getSimpleName() + "@" + getUriAsString(true);
}
}

View File

@@ -1,91 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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.io;
import org.springframework.data.keyvalue.riak.core.RiakTemplate;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* An {@link java.io.InputStream} implementation that is backed by a resource residing in Riak.
*
* @author J. Brisbin <jon@jbrisbin.com>
*/
public class RiakInputStream<B, K> extends InputStream {
private RiakTemplate riak;
private B bucket;
private K key;
private ByteArrayInputStream in;
public RiakInputStream(RiakTemplate riak, B bucket, K key) {
this.riak = riak;
this.bucket = bucket;
this.key = key;
this.in = new ByteArrayInputStream(riak.getAsBytes(bucket, key));
}
@Override
public int read(byte[] bytes) throws IOException {
return in.read(bytes);
}
@Override
public int read(byte[] bytes, int i, int i1) throws IOException {
return in.read(bytes, i, i1);
}
@Override
public long skip(long l) throws IOException {
return in.skip(l);
}
@Override
public int available() throws IOException {
return in.available();
}
@Override
public void close() throws IOException {
in.close();
}
@Override
public void mark(int i) {
in.mark(i);
}
@Override
public void reset() throws IOException {
in.reset();
}
@Override
public boolean markSupported() {
return in.markSupported();
}
@Override
public int read() throws IOException {
return in.read();
}
}

View File

@@ -1,46 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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.io;
import org.springframework.data.keyvalue.riak.core.RiakTemplate;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
/**
* @author J. Brisbin <jon@jbrisbin.com>
*/
public class RiakOutputStream<B, K> extends ByteArrayOutputStream {
private RiakTemplate riak;
private B bucket;
private K key;
public RiakOutputStream(RiakTemplate riak, B bucket, K key) {
this.riak = riak;
this.bucket = bucket;
this.key = key;
}
@Override
public void flush() throws IOException {
super.flush();
riak.setAsBytes(bucket, key, toByteArray());
}
}

View File

@@ -1,138 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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.io;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.data.keyvalue.riak.core.RiakTemplate;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
/**
* An implementation of {@link org.springframework.core.io.UrlResource} that is backed by a
* resource in Riak.
*
* @author J. Brisbin <jon@jbrisbin.com>
*/
public class RiakResource<B, K> extends UrlResource {
protected final Log log = LogFactory.getLog(getClass());
private RiakTemplate riak;
private B bucket;
private K key;
private String description;
public RiakResource(RiakTemplate riak, B bucket, K key) throws MalformedURLException {
super(riak.getDefaultUri());
this.bucket = bucket;
this.key = key;
}
public RiakTemplate getRiakTemplate() {
return this.riak;
}
public B getBucket() {
return bucket;
}
public K getKey() {
return key;
}
@SuppressWarnings({"unchecked"})
@Override
public URL getURL() throws IOException {
try {
return new URL(new RiakFile(riak, bucket, key).getUriAsString(true));
} catch (URISyntaxException e) {
log.error(e.getMessage(), e);
}
return null;
}
@SuppressWarnings({"unchecked"})
@Override
public URI getURI() throws IOException {
try {
return new RiakFile(riak, bucket, key).toURI();
} catch (URISyntaxException e) {
log.error(e.getMessage(), e);
}
return null;
}
@SuppressWarnings({"unchecked"})
@Override
public Resource createRelative(String relativePath) throws MalformedURLException {
if (relativePath.startsWith("../")) {
return new RiakResource(riak, bucket, relativePath.substring(3));
} else if (!relativePath.startsWith("/")) {
return new RiakResource(riak, bucket, relativePath);
}
return null;
}
@SuppressWarnings({"unchecked"})
@Override
public String getFilename() {
try {
return new RiakFile(riak, bucket, key).getUriAsString(true);
} catch (URISyntaxException e) {
log.error(e.getMessage(), e);
}
return riak.getDefaultUri();
}
public void setDescription(String description) {
this.description = description;
}
@Override
public String getDescription() {
return this.description;
}
@SuppressWarnings({"unchecked"})
@Override
public File getFile() throws IOException {
try {
return new RiakFile(riak, bucket, key);
} catch (URISyntaxException e) {
log.error(e.getMessage(), e);
}
return null;
}
@SuppressWarnings({"unchecked"})
@Override
public InputStream getInputStream() throws IOException {
return new RiakInputStream(riak, bucket, key);
}
}

View File

@@ -1,15 +0,0 @@
<html>
<body>
<p>
Utilities for working with resources stored in Riak as standard java.io objects. Opening a <a
href="RiakInputStream.html">RiakInputStream</a> to a resource will allow code that doesn't
know anything about Key/Value datastores to access resources stored within them.
</p>
<p>
Alternatively, writing data to a <a href="RiakOutputStream.html">RiakOutputStream</a> will
create a resources in Riak without exposing any of the underlying data access code to the
calling application.
</p>
</body>
</html>

View File

@@ -1,7 +0,0 @@
<html>
<body>
<p>
Root package for the core utilities that make up the Riak data access library.
</p>
</body>
</html>

View File

@@ -1,463 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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.groovy;
import groovy.lang.Closure;
import groovy.util.BuilderSupport;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.keyvalue.riak.DataStoreOperationException;
import org.springframework.data.keyvalue.riak.core.AsyncRiakTemplate;
import org.springframework.data.keyvalue.riak.core.RiakQosParameters;
import org.springframework.data.keyvalue.riak.core.SimpleBucketKeyPair;
import org.springframework.data.keyvalue.riak.mapreduce.*;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* A Groovy Builder that implements a powerful and syntactically succinct DSL for Riak datastore
* access using SDKV for Riak's {@link AsyncRiakTemplate}.
* <p/>
* The DSL responds to most of the important methods from the <code>AsyncRiakTemplate</code>:
* <ul><li>set</li><li>setAsBytes</li><li>put</li><li>get</li><li>getAsBytes</li>
* <li>getAsType</li><li>containsKey</li><li>delete</li><li>foreach</li></ul>
* <p/>
* An example of DSL usage (to delete all entries in a bucket):
* <pre><code>riak.foreach(bucket: "test") {
* completed { v, meta ->
* delete(bucket: "test", key: meta.key)
* }
* }
* </code></pre>
*
* @author J. Brisbin <jon@jbrisbin.com>
*/
public class RiakBuilder extends BuilderSupport {
private static enum NodeName {
CALL, FOREACH, MAPREDUCE, QUERY, MAP, REDUCE, INPUTS, LANGUAGE, SOURCE, KEEP, ARG, COMPLETED, FAILED
}
protected final Log log = LogFactory.getLog(getClass());
@Autowired(required = false)
protected AsyncRiakTemplate riak;
@Autowired(required = false)
protected ExecutorService workerPool = Executors.newCachedThreadPool();
protected String defaultBucketName;
protected List<Object> results = new LinkedList<Object>();
public RiakBuilder() {
}
public RiakBuilder(AsyncRiakTemplate riak) {
this.riak = riak;
}
public RiakBuilder(AsyncRiakTemplate riak, ExecutorService workerPool) {
this.riak = riak;
this.workerPool = workerPool;
}
public RiakBuilder(BuilderSupport proxyBuilder, AsyncRiakTemplate riak) {
super(proxyBuilder);
this.riak = riak;
}
public RiakBuilder(Closure nameMappingClosure, BuilderSupport proxyBuilder,
AsyncRiakTemplate riak) {
super(nameMappingClosure, proxyBuilder);
this.riak = riak;
}
public AsyncRiakTemplate getAsyncTemplate() {
return riak;
}
public void setAsyncTemplate(AsyncRiakTemplate riak) {
this.riak = riak;
}
public ExecutorService getWorkerPool() {
return workerPool;
}
public void setWorkerPool(ExecutorService workerPool) {
this.workerPool = workerPool;
}
@Override
protected void setParent(Object parent, Object child) {
// log.debug("setParent/2 " + parent + " " + child);
}
@SuppressWarnings({"unchecked"})
@Override
protected Object createNode(Object name) {
// log.debug("createNode/1 " + name);
NodeName nodeName = null;
try {
nodeName = NodeName.valueOf(name.toString().toUpperCase());
} catch (IllegalArgumentException e) {
// IGNORED
}
if (null != nodeName) {
QueryPhase p;
switch (nodeName) {
case CALL:
break;
case FOREACH:
RiakOperation<Object> op = new RiakOperation<Object>(riak,
RiakOperation.Type.FOREACH);
op.setBucket(defaultBucketName);
return op;
case MAPREDUCE:
return createMapReduceJob();
case QUERY:
p = new QueryPhase();
p.job = ((RiakMapReduceOperation) getCurrent()).getJob();
return getCurrent();
case REDUCE:
case MAP:
p = new QueryPhase();
p.job = ((RiakMapReduceOperation) getCurrent()).getJob();
p.phase = name.toString();
return p;
}
} else {
defaultBucketName = name.toString();
}
return null;
}
@SuppressWarnings({"unchecked"})
@Override
protected Object createNode(Object name, Object value) {
// log.debug("createNode/2 " + name + " " + value);
NodeName nodeName = null;
try {
nodeName = NodeName.valueOf(name.toString().toUpperCase());
} catch (IllegalArgumentException e) {
// IGNORED
}
if (null != nodeName) {
QueryPhase p;
switch (nodeName) {
case INPUTS:
AsyncRiakMapReduceJob job = ((RiakMapReduceOperation) getCurrent()).getJob();
if (null != value && value instanceof String) {
List<String> keys = new ArrayList<String>();
keys.add(value.toString());
job.addInputs(keys);
} else if (value instanceof List) {
job.addInputs((List) value);
}
return job;
case LANGUAGE:
p = (QueryPhase) getCurrent();
p.language = value.toString();
return p;
case SOURCE:
p = (QueryPhase) getCurrent();
p.source = value.toString();
return p;
case KEEP:
p = (QueryPhase) getCurrent();
p.keep = (value instanceof Boolean ? (Boolean) value : new Boolean(value.toString()));
return p;
case ARG:
p = (QueryPhase) getCurrent();
p.arg = value;
return p;
}
}
return null;
}
@SuppressWarnings({"unchecked"})
@Override
protected Object createNode(Object name, Map attributes) {
// log.debug("createNode/2 (Map) " + name + " " + attributes);
NodeName nodeName = null;
try {
nodeName = NodeName.valueOf(name.toString().toUpperCase());
} catch (IllegalArgumentException e) {
// IGNORED
}
if (null != nodeName) {
switch (nodeName) {
case MAPREDUCE:
RiakMapReduceOperation oper = createMapReduceJob();
// Set timeout
Object o = attributes.get("wait");
if (null != o) {
if (o instanceof Long) {
oper.setTimeout((Long) o);
} else if (o instanceof String) {
oper.setTimeout(new Long(o.toString()));
} else if (o instanceof Integer) {
oper.setTimeout(new Long((Integer) o));
} else {
throw new IllegalArgumentException(
"Timeout should be an Integer, a Long, or a String denoting milliseconds");
}
}
return oper;
case MAP:
case REDUCE:
QueryPhase p = new QueryPhase();
p.job = ((RiakMapReduceOperation) getCurrent()).getJob();
p.phase = name.toString();
// Set arg
p.arg = attributes.get("arg");
return p;
}
}
RiakOperation.Type type = null;
try {
type = RiakOperation.Type.valueOf(name.toString().toUpperCase());
} catch (IllegalArgumentException ignored) {
// IGNORED
}
if (null != type) {
RiakOperation op = new RiakOperation<Object>(riak, type);
// Set a bucket name
Object o = attributes.get("bucket");
if (null == o && null != defaultBucketName) {
op.setBucket(defaultBucketName);
} else {
op.setBucket((null != o ? o.toString() : null));
}
// Set the object's key
o = attributes.get("key");
op.setKey((null != o ? o.toString() : null));
// Set the value
o = attributes.get("value");
op.setValue(o);
// Set the type of object (for getAsType)
o = attributes.get("type");
if (null != o) {
if (o instanceof Class) {
op.setRequiredType((Class<?>) o);
} else if (o instanceof String) {
try {
op.setRequiredType(Class.forName((String) o));
} catch (ClassNotFoundException e) {
throw new DataStoreOperationException(e.getMessage(), e);
}
} else {
op.setRequiredType(o.getClass());
}
}
// Set QOS parameters
o = attributes.get("qos");
if (null != o) {
RiakQosParameters qos = new RiakQosParameters();
Map<String, Object> qosParams = (Map<String, Object>) o;
if (qosParams.containsKey("dw")) {
qos.setDurableWriteThreshold(qosParams.get("dw"));
}
if (qosParams.containsKey("w")) {
qos.setWriteThreshold(qosParams.get("w"));
}
if (qosParams.containsKey("r")) {
qos.setReadThreshold(qosParams.get("r"));
}
op.setQosParameters(qos);
}
// Set timeout
o = attributes.get("wait");
if (null != o) {
if (o instanceof Long) {
op.setTimeout((Long) o);
} else if (o instanceof String) {
op.setTimeout(new Long(o.toString()));
} else if (o instanceof Integer) {
op.setTimeout(new Long((Integer) o));
} else {
throw new IllegalArgumentException(
"Timeout should be an Integer, a Long, or a String denoting milliseconds");
}
}
return op;
}
return null;
}
@Override
protected Object createNode(Object name, Map attributes, Object value) {
// log.debug("createNode/3");
return null;
}
@SuppressWarnings({"unchecked"})
@Override
public Object invokeMethod(String methodName, Object arg) {
// if (log.isDebugEnabled()) {
// log.debug("invokeMethod/2: " + methodName + " " + arg);
// }
NodeName nodeName = null;
try {
nodeName = NodeName.valueOf(methodName.toString().toUpperCase());
} catch (IllegalArgumentException e) {
// IGNORED
}
if (null != nodeName) {
switch (nodeName) {
case COMPLETED:
case FAILED:
if (getCurrent() instanceof RiakOperation) {
RiakOperation<Object> op = (RiakOperation<Object>) getCurrent();
Object[] args = (Object[]) arg;
Map<String, Object> params;
Closure handler = null;
Closure guard = null;
for (Object o : args) {
if (o instanceof Map) {
params = (Map<String, Object>) o;
if (params.containsKey("when")) {
guard = (Closure) params.get("when");
}
} else if (o instanceof Closure) {
handler = (Closure) o;
}
}
op.addHandler(methodName, handler, guard);
return op;
} else if (getCurrent() instanceof RiakMapReduceOperation) {
RiakMapReduceOperation oper = (RiakMapReduceOperation) getCurrent();
Object[] args = (Object[]) arg;
if ("completed".equals(methodName)) {
oper.setCompleted((Closure) args[0]);
} else if ("failed".equals(methodName)) {
oper.setFailed((Closure) args[0]);
}
return oper;
}
break;
case CALL:
results.clear();
defaultBucketName = null;
}
}
// By default
return super.invokeMethod(methodName, arg);
}
@SuppressWarnings({"unchecked"})
@Override
protected void nodeCompleted(Object parent, Object node) {
// if (log.isDebugEnabled()) {
// log.debug("nodeCompleted: parent=" + parent + ", node=" + node);
// }
if (parent instanceof RiakMapReduceOperation && node instanceof QueryPhase) {
QueryPhase p = (QueryPhase) node;
MapReduceOperation oper = null;
if ("javascript".equals(p.language)) {
if (null != p.source) {
oper = new JavascriptMapReduceOperation(p.source);
} else if (null != p.bucket && null != p.key) {
oper = new JavascriptMapReduceOperation(new SimpleBucketKeyPair(p.bucket, p.key));
}
} else {
oper = new ErlangMapReduceOperation(p.module, p.func);
}
if (null != oper) {
RiakMapReducePhase phase = new RiakMapReducePhase(p.phase, p.language, oper);
if (null != p.keep) {
phase.setKeepResults(p.keep);
}
phase.setArg(p.arg);
p.job.addPhase(phase);
}
} else {
super.nodeCompleted(parent, node);
}
}
@SuppressWarnings({"unchecked"})
@Override
protected Object postNodeCompletion(Object parent, Object node) {
// if (log.isDebugEnabled()) {
// log.debug("postNodeCompletion: " + parent + " " + node);
// }
if (node instanceof RiakOperation) {
RiakOperation<Object> op = (RiakOperation<Object>) node;
try {
Object o = op.call();
if (null != o && !o.equals(results)) {
results.add(o);
}
return o;
} catch (Exception e) {
log.error(e.getMessage(), e);
}
} else if (null == parent && node instanceof RiakMapReduceOperation) {
RiakMapReduceOperation oper = (RiakMapReduceOperation) node;
try {
Object o = oper.call();
if (null != o) {
results.add(o);
}
return o;
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
return super.postNodeCompletion(parent, node);
}
protected RiakMapReduceOperation createMapReduceJob() {
AsyncRiakMapReduceJob job = new AsyncRiakMapReduceJob(riak);
if (null != defaultBucketName) {
List<String> keys = new ArrayList<String>();
keys.add(defaultBucketName);
job.addInputs(keys);
}
return new RiakMapReduceOperation(riak, job);
}
private class QueryPhase {
AsyncRiakMapReduceJob job;
String phase;
String language = "javascript";
String source = null;
String bucket = null;
String key = null;
String module = null;
String func = null;
Boolean keep = null;
Object arg = null;
}
}

View File

@@ -1,115 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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.groovy;
import groovy.lang.Closure;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.keyvalue.riak.core.AsyncKeyValueStoreOperation;
import org.springframework.data.keyvalue.riak.core.AsyncRiakTemplate;
import org.springframework.data.keyvalue.riak.core.KeyValueStoreMetaData;
import org.springframework.data.keyvalue.riak.mapreduce.AsyncRiakMapReduceJob;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
/**
* @author J. Brisbin <jon@jbrisbin.com>
*/
public class RiakMapReduceOperation implements Callable {
protected final Log log = LogFactory.getLog(getClass());
protected AsyncRiakTemplate riak;
protected AsyncRiakMapReduceJob job;
protected Long timeout = -1L;
protected Closure completed = null;
protected Closure failed = null;
public RiakMapReduceOperation(AsyncRiakTemplate riak, AsyncRiakMapReduceJob job) {
this.riak = riak;
this.job = job;
}
public AsyncRiakMapReduceJob getJob() {
return job;
}
public void setJob(AsyncRiakMapReduceJob job) {
this.job = job;
}
public Long getTimeout() {
return timeout;
}
public void setTimeout(Long timeout) {
this.timeout = timeout;
}
public Closure getCompleted() {
return completed;
}
public void setCompleted(Closure completed) {
this.completed = completed;
}
public Closure getFailed() {
return failed;
}
public void setFailed(Closure failed) {
this.failed = failed;
}
public Object call() throws Exception {
Future<?> f = riak.execute(job, new AsyncKeyValueStoreOperation<List<?>, Object>() {
public Object completed(KeyValueStoreMetaData meta, List<?> result) {
if (null != completed) {
if (completed.getParameterTypes().length == 2) {
return completed.call(new Object[]{result, meta});
} else {
return completed.call(result);
}
} else {
return new Object[]{result, meta};
}
}
public Object failed(Throwable error) {
if (null != failed) {
return failed.call(error);
} else {
throw new RuntimeException(error);
}
}
});
if (timeout == 0) {
return f;
} else if (timeout < 0) {
return f.get();
} else {
return f.get(timeout, TimeUnit.MILLISECONDS);
}
}
}

View File

@@ -1,303 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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.groovy;
import groovy.lang.Closure;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.keyvalue.riak.DataStoreOperationException;
import org.springframework.data.keyvalue.riak.core.AsyncKeyValueStoreOperation;
import org.springframework.data.keyvalue.riak.core.AsyncRiakTemplate;
import org.springframework.data.keyvalue.riak.core.KeyValueStoreMetaData;
import org.springframework.data.keyvalue.riak.core.QosParameters;
import java.util.*;
import java.util.concurrent.*;
/**
* @author J. Brisbin <jon@jbrisbin.com>
*/
public class RiakOperation<T> implements Callable {
static enum Type {
SET, SETASBYTES, PUT, GET, GETASBYTES, GETASTYPE, CONTAINSKEY, DELETE, FOREACH
}
static String COMPLETED = "completed";
static String FAILED = "failed";
protected final Log log = LogFactory.getLog(getClass());
protected AsyncRiakTemplate riak;
protected Type type;
protected String bucket;
protected String key;
protected T value;
protected Class<?> requiredType = null;
protected long timeout = -1L;
protected QosParameters qosParameters;
protected Map<String, List<GuardedClosure>> callbacks = new LinkedHashMap<String, List<GuardedClosure>>();
protected ClosureInvokingCallback callbackInvoker = new ClosureInvokingCallback();
public RiakOperation(AsyncRiakTemplate riak, Type type) {
this.riak = riak;
this.type = type;
}
public Type getType() {
return type;
}
public Map<String, List<GuardedClosure>> getCallbacks() {
return callbacks;
}
public QosParameters getQosParameters() {
return qosParameters;
}
public void setQosParameters(QosParameters qosParameters) {
this.qosParameters = qosParameters;
}
public String getBucket() {
return bucket;
}
public void setBucket(String bucket) {
this.bucket = bucket;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
public Class<?> getRequiredType() {
return requiredType;
}
public void setRequiredType(Class<?> requiredType) {
this.requiredType = requiredType;
}
public long getTimeout() {
return timeout;
}
public void setTimeout(long timeout) {
this.timeout = timeout;
}
public void addHandler(String type, Closure handler, Closure guard) {
List<GuardedClosure> guardedClosures = callbacks.get(type);
if (null == guardedClosures) {
guardedClosures = new ArrayList<GuardedClosure>();
callbacks.put(type, guardedClosures);
}
guardedClosures.add(new GuardedClosure(handler, guard));
}
@SuppressWarnings({"unchecked"})
public Object call() throws Exception {
Future<?> f = null;
switch (type) {
case GET:
f = riak.get(bucket, key, callbackInvoker);
break;
case GETASBYTES:
f = riak.getAsBytes(bucket, key, callbackInvoker);
break;
case GETASTYPE:
f = riak.getAsType(bucket, key, requiredType, callbackInvoker);
break;
case PUT:
f = riak.put(bucket, value, callbackInvoker);
break;
case SET:
f = riak.set(bucket, key, value, callbackInvoker);
break;
case SETASBYTES:
byte[] bytes;
if (value instanceof byte[]) {
bytes = (byte[]) value;
} else {
bytes = riak.getConversionService().convert(value, byte[].class);
}
f = riak.setAsBytes(bucket, key, bytes, callbackInvoker);
break;
case CONTAINSKEY:
f = riak.containsKey(bucket, key, callbackInvoker);
break;
case DELETE:
f = riak.delete(bucket, key, callbackInvoker);
break;
case FOREACH:
f = riak.getBucketSchema(bucket,
null,
new AsyncKeyValueStoreOperation<Map<String, Object>, Object>() {
public Object completed(KeyValueStoreMetaData meta, Map<String, Object> result) {
List<Object> results = new LinkedList<Object>();
List<String> keys = (List<String>) result.get("keys");
for (String key : keys) {
try {
Future<?> getFut = riak.get(bucket, key, callbackInvoker);
if (timeout > 0) {
Object o = getFut.get(timeout, TimeUnit.MILLISECONDS);
if (null != o) {
results.add(o);
}
} else if (timeout < 0) {
Object o = getFut.get();
if (null != o) {
results.add(o);
}
} else {
results.add(getFut);
}
} catch (InterruptedException e) {
throw new DataStoreOperationException(e.getMessage(), e);
} catch (ExecutionException e) {
throw new DataStoreOperationException(e.getMessage(), e);
} catch (TimeoutException e) {
throw new DataStoreOperationException(e.getMessage(), e);
}
}
return (results.size() > 0 ? results : null);
}
public Object failed(Throwable error) {
throw new RuntimeException(error);
}
});
break;
}
if (null != f) {
if (timeout > 0) {
// Block until finished or timeout
return f.get(timeout, TimeUnit.MILLISECONDS);
} else if (timeout < 0) {
// Block indefinitely
return f.get();
}
}
return f;
}
class GuardedClosure {
private Closure delegate;
private Closure guard;
GuardedClosure(Closure delegate, Closure guard) {
this.delegate = delegate;
this.guard = guard;
}
public Closure getDelegate() {
return delegate;
}
public Closure getGuard() {
return guard;
}
}
class ClosureInvokingCallback implements AsyncKeyValueStoreOperation {
public Object completed(KeyValueStoreMetaData meta, Object result) {
if (!callbacks.containsKey(COMPLETED)) {
return new Object[]{result, meta};
}
for (GuardedClosure cl : callbacks.get(COMPLETED)) {
boolean execute = true;
Closure guardExpr = cl.getGuard();
if (null != guardExpr) {
int noOfParams = guardExpr.getParameterTypes().length;
Object guardResult;
if (noOfParams == 2) {
guardResult = guardExpr.call(new Object[]{result, meta});
} else {
guardResult = guardExpr.call(result);
}
if (null != guardResult) {
if (guardResult instanceof Boolean) {
execute = (Boolean) guardResult;
} else {
execute = true;
}
}
}
if (execute) {
Closure callback = cl.getDelegate();
if (callback.getParameterTypes().length == 2) {
return callback.call(new Object[]{result, meta});
} else {
return callback.call(result);
}
}
}
return null;
}
public Object failed(Throwable error) {
if (!callbacks.containsKey(FAILED)) {
throw new RuntimeException(error);
}
for (GuardedClosure cl : callbacks.get(FAILED)) {
boolean execute = true;
Closure guardExpr = cl.getGuard();
if (null != guardExpr) {
Object guardResult = guardExpr.call(error);
if (null != guardResult) {
if (guardResult instanceof Boolean) {
execute = (Boolean) guardResult;
} else {
execute = true;
}
}
}
if (execute) {
Closure callback = cl.getDelegate();
return callback.call(error);
}
}
return null;
}
}
}

View File

@@ -1,8 +0,0 @@
<html>
<body>
<p>
Utilities for making Riak data access easier in Groovy. The <a href="RiakBuilder.html">RiakBuilder</a>
provides a Groovy DSL for interacting with Riak.
</p>
</body>
</html>

View File

@@ -1,144 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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.mapreduce;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.data.keyvalue.riak.core.BucketKeyPair;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* An implementation of {@link MapReduceJob} for the Riak data store.
*
* @author J. Brisbin <jon@jbrisbin.com>
*/
@SuppressWarnings({"unchecked"})
public abstract class AbstractRiakMapReduceJob implements MapReduceJob {
protected final Log log = LogFactory.getLog(getClass());
protected List<Object> inputs = new LinkedList<Object>();
protected List<MapReducePhase> phases = new ArrayList<MapReducePhase>();
public List getInputs() {
return this.inputs;
}
public MapReduceJob addInputs(List keys) {
inputs.addAll(keys);
return this;
}
public MapReduceJob addPhase(MapReducePhase phase) {
phases.add(phase);
return this;
}
public List<MapReducePhase> getPhases() {
return this.phases;
}
public String toJson() {
StringWriter out = new StringWriter();
try {
JsonGenerator json = new JsonFactory().createJsonGenerator(out);
json.setCodec(new ObjectMapper());
json.writeStartObject();
// Inputs
json.writeFieldName("inputs");
if (1 == inputs.size() && !(inputs.get(0) instanceof List)) {
json.writeString(inputs.get(0).toString());
} else if (inputs.size() > 0) {
json.writeStartArray();
for (Object obj : inputs) {
List pair = (List) obj;
json.writeStartArray();
json.writeString(pair.get(0).toString());
json.writeString(pair.get(1).toString());
json.writeEndArray();
}
json.writeEndArray();
}
// Query
json.writeFieldName("query");
json.writeStartArray();
for (MapReducePhase phase : phases) {
json.writeStartObject();
switch (phase.getPhase()) {
case MAP:
json.writeFieldName("map");
break;
case REDUCE:
json.writeFieldName("reduce");
break;
case LINK:
json.writeFieldName("link");
}
json.writeStartObject();
json.writeStringField("language", phase.getLanguage());
Object repr = phase.getOperation().getRepresentation();
if (repr instanceof String) {
// Using source
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("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());
}
}
if (phase.getKeepResults()) {
json.writeBooleanField("keep", true);
}
// Arg
if (null != phase.getArg()) {
json.writeObjectField("arg", phase.getArg());
}
json.writeEndObject();
json.writeEndObject();
}
json.writeEndArray();
json.writeEndObject();
json.flush();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
return out.toString();
}
}

View File

@@ -1,41 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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.mapreduce;
import org.springframework.data.keyvalue.riak.core.AsyncKeyValueStoreOperation;
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 AsyncMapReduceOperations {
/**
* Execute a {@link MapReduceJob} synchronously.
*
* @param job
* @return
*/
<R> Future<?> execute(MapReduceJob job, AsyncKeyValueStoreOperation<List<?>, R> callback);
}

View File

@@ -1,48 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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.mapreduce;
import org.springframework.data.keyvalue.riak.core.AsyncRiakTemplate;
/**
* An implementation of {@link MapReduceJob} for the Riak data store.
*
* @author J. Brisbin <jon@jbrisbin.com>
*/
@SuppressWarnings({"unchecked"})
public class AsyncRiakMapReduceJob extends AbstractRiakMapReduceJob {
protected AsyncRiakTemplate riakTemplate;
public AsyncRiakMapReduceJob(AsyncRiakTemplate riakTemplate) {
this.riakTemplate = riakTemplate;
}
public AsyncRiakTemplate getAsyncRiakTemplate() {
return riakTemplate;
}
public void setAsyncRiakTemplate(AsyncRiakTemplate riakTemplate) {
this.riakTemplate = riakTemplate;
}
public Object call() throws Exception {
return riakTemplate.execute(this, null);
}
}

View File

@@ -1,67 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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.mapreduce;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* An implementation of {@link org.springframework.data.keyvalue.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"})
public class ErlangMapReduceOperation implements MapReduceOperation {
protected String language = "erlang";
protected Map moduleFunction = new LinkedHashMap();
public ErlangMapReduceOperation() {
}
public ErlangMapReduceOperation(String module, String function) {
setModule(module);
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);
}
public Object getRepresentation() {
return moduleFunction;
}
}

View File

@@ -1,73 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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.mapreduce;
import org.springframework.data.keyvalue.riak.core.BucketKeyPair;
/**
* An implementation of {@link org.springframework.data.keyvalue.riak.mapreduce.MapReduceOperation}
* to describe a Javascript language M/R function.
*
* @author J. Brisbin <jon@jbrisbin.com>
*/
public class JavascriptMapReduceOperation implements MapReduceOperation {
protected String source;
protected BucketKeyPair bucketKeyPair;
public JavascriptMapReduceOperation(String source) {
this.source = source;
}
public JavascriptMapReduceOperation(BucketKeyPair bucketKeyPair) {
this.bucketKeyPair = bucketKeyPair;
}
public String getSource() {
return source;
}
/**
* Set the anonymous source to use for the M/R function.
*
* @param source
*/
public void setSource(String source) {
this.source = source;
}
public BucketKeyPair getBucketKeyPair() {
return bucketKeyPair;
}
/**
* Set the {@link org.springframework.data.keyvalue.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;
}
public Object getRepresentation() {
return (null != bucketKeyPair ? bucketKeyPair : source);
}
}

View File

@@ -1,69 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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.mapreduce;
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 {
/**
* 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);
/**
* Get the list of phases for this job.
*
* @return
*/
List<MapReducePhase> getPhases();
/**
* Convert this job into the appropriate JSON to send to the server.
*
* @return
*/
String toJson();
}

View File

@@ -1,26 +0,0 @@
package org.springframework.data.keyvalue.riak.mapreduce;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author J. Brisbin <jbrisbin@vmware.com>
*/
public class MapReduceLinkOperation implements MapReduceOperation {
protected String bucket = null;
protected String key;
public MapReduceLinkOperation(String bucket, String key) {
this.bucket = bucket;
this.key = key;
}
public Object getRepresentation() {
Map<String, Object> repr = new LinkedHashMap<String, Object>();
repr.put("bucket", (null != bucket ? bucket : "_"));
repr.put("key", (null != key ? key : "_"));
return repr;
}
}

View File

@@ -1,35 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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.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();
}

View File

@@ -1,58 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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.mapreduce;
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.data.keyvalue.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);
}

View File

@@ -1,82 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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.mapreduce;
/**
* A generic interface to the phases of Map/Reduce jobs.
*
* @author J. Brisbin <jon@jbrisbin.com>
*/
public interface MapReducePhase {
public static enum Phase {
MAP, REDUCE, LINK
}
/**
* The bucket pattern to match on link phases.
*
* @return
*/
String getBucket();
/**
* Set the bucket pattern to match on link phases.
*
* @param bucket
*/
void setBucket(String bucket);
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();
/**
* Set the static argument for this job.
*
* @param arg
*/
void setArg(Object arg);
/**
* Get the static argument for this phase.
*
* @return
*/
Object getArg();
}

View File

@@ -1,49 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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.mapreduce;
import org.springframework.data.keyvalue.riak.core.RiakTemplate;
/**
* An implementation of {@link org.springframework.data.keyvalue.riak.mapreduce.MapReduceJob}
* for the Riak data store.
*
* @author J. Brisbin <jon@jbrisbin.com>
*/
@SuppressWarnings({"unchecked"})
public class RiakMapReduceJob extends AbstractRiakMapReduceJob {
protected RiakTemplate riakTemplate;
public RiakMapReduceJob(RiakTemplate riakTemplate) {
this.riakTemplate = riakTemplate;
}
public RiakTemplate getRiakTemplate() {
return riakTemplate;
}
public void setRiakTemplate(RiakTemplate riakTemplate) {
this.riakTemplate = riakTemplate;
}
public Object call() throws Exception {
return riakTemplate.execute(this);
}
}

View File

@@ -1,88 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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.mapreduce;
/**
* An implementation of {@link org.springframework.data.keyvalue.riak.mapreduce.MapReducePhase}
* for the Riak data store.
*
* @author J. Brisbin <jon@jbrisbin.com>
*/
public class RiakMapReducePhase implements MapReducePhase {
protected Phase phase;
protected String bucket;
protected String language;
protected MapReduceOperation operation;
protected boolean keepResults = false;
protected Object arg;
public RiakMapReducePhase(String phase, String language, MapReduceOperation oper) {
this.phase = Phase.valueOf(phase.toUpperCase());
this.language = language;
this.operation = oper;
}
public RiakMapReducePhase(Phase phase, String language, MapReduceOperation oper) {
this.phase = phase;
this.language = language;
this.operation = oper;
}
public String getBucket() {
return this.bucket;
}
public void setBucket(String bucket) {
this.bucket = bucket;
}
public Phase getPhase() {
return phase;
}
public String getLanguage() {
return language;
}
public MapReduceOperation getOperation() {
return this.operation;
}
public boolean getKeepResults() {
return this.keepResults;
}
public void setKeepResults(boolean keepResults) {
this.keepResults = keepResults;
}
public void setOperation(MapReduceOperation oper) {
this.operation = oper;
}
public Object getArg() {
return arg;
}
public void setArg(Object arg) {
this.arg = arg;
}
}

View File

@@ -1,7 +0,0 @@
<html>
<body>
<p>
Root package for
</p>
</body>
</html>

View File

@@ -1,8 +0,0 @@
<html>
<body>
<p>
Root package for integrating <a href="http://www.basho.com/Riak.html">Riak</a> with Spring
concepts.
</p>
</body>
</html>

View File

@@ -1,49 +0,0 @@
/*
* Copyright (c) 2011 by J. Brisbin <jon@jbrisbin.com>
* Portions (c) 2011 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.util;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.DefaultResponseErrorHandler;
import java.io.IOException;
/**
* @author J. Brisbin <jon@jbrisbin.com>
*/
public class Ignore404sErrorHandler extends DefaultResponseErrorHandler {
@Override
protected boolean hasError(HttpStatus statusCode) {
if (statusCode != HttpStatus.NOT_FOUND) {
return super.hasError(statusCode);
} else {
return false;
}
}
@Override
public void handleError(ClientHttpResponse response) throws IOException {
// Ignore 404s entirely
if (response.getStatusCode() != HttpStatus.NOT_FOUND) {
super.handleError(response);
}
}
}

View File

@@ -1,153 +0,0 @@
/*
* Copyright (c) 2011 by J. Brisbin <jon@jbrisbin.com>
* Portions (c) 2011 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.util;
import org.apache.commons.cli.*;
import org.springframework.data.keyvalue.riak.core.RiakTemplate;
import java.io.*;
import java.net.URLEncoder;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
* @author J. Brisbin <jon@jbrisbin.com>
*/
public class RiakClassFileLoader {
static Options opts = new Options();
static {
opts.addOption("v", false, "Verbose output");
opts.addOption("u",
true,
"URL to Riak (defaults to: 'http://localhost:8098/riak/{bucket}/{key}')");
opts.addOption("b", true, "Bucket to load class files into");
opts.addOption("k", true, "Key under which to store an individual class file");
opts.addOption("j", true, "JAR file to load into Riak");
opts.addOption("c", true, "Class file to load into Riak");
opts.addOption("d", true, "Directory from which to load all JAR files into Riak");
}
public static void main(String[] args) {
Parser p = new BasicParser();
CommandLine cl = null;
try {
cl = p.parse(opts, args);
} catch (ParseException e) {
System.err.println("Error parsing command line: " + e.getMessage());
}
if (null != cl) {
boolean verbose = cl.hasOption('v');
RiakTemplate riak = new RiakTemplate();
riak.getRestTemplate().setErrorHandler(new Ignore404sErrorHandler());
if (cl.hasOption('u')) {
riak.setDefaultUri(cl.getOptionValue('u'));
}
try {
riak.afterPropertiesSet();
} catch (Exception e) {
System.err.println("Error creating RiakTemplate: " + e.getMessage());
}
String[] files = cl.getOptionValues('j');
if (null != files) {
for (String file : files) {
if (verbose) {
System.out.println(String.format("Loading JAR file %s into Riak...", file));
}
try {
File zfile = new File(file);
ZipInputStream zin = new ZipInputStream(new FileInputStream(zfile));
ZipEntry entry;
while (null != (entry = zin.getNextEntry())) {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
byte[] buff = new byte[16384];
for (int bytesRead = zin.read(buff); bytesRead > 0; bytesRead = zin.read(buff)) {
bout.write(buff, 0, bytesRead);
}
if (entry.getName().endsWith(".class")) {
String name = entry.getName().replaceAll("/", ".");
name = URLEncoder.encode(name.substring(0, name.length() - 6), "UTF-8");
String bucket;
if (cl.hasOption('b')) {
bucket = cl.getOptionValue('b');
} else {
bucket = URLEncoder.encode(zfile.getCanonicalFile().getName(), "UTF-8");
}
if (verbose) {
System.out.println(String.format("Uploading to %s/%s", bucket, name));
}
// Load these bytes into Riak
riak.setAsBytes(bucket, name, bout.toByteArray());
}
}
} catch (FileNotFoundException e) {
System.err.println("Error reading JAR file: " + e.getMessage());
} catch (IOException e) {
System.err.println("Error reading JAR file: " + e.getMessage());
}
}
}
String[] classFiles = cl.getOptionValues('c');
if (null != classFiles) {
for (String classFile : classFiles) {
try {
FileInputStream fin = new FileInputStream(classFile);
ByteArrayOutputStream bout = new ByteArrayOutputStream();
byte[] buff = new byte[16384];
for (int bytesRead = fin.read(buff); bytesRead > 0; bytesRead = fin.read(buff)) {
bout.write(buff, 0, bytesRead);
}
String name;
if (cl.hasOption('k')) {
name = cl.getOptionValue('k');
} else {
throw new IllegalStateException(
"Must specify a Riak key in which to store the data if loading individual class files.");
}
String bucket;
if (cl.hasOption('b')) {
bucket = cl.getOptionValue('b');
} else {
throw new IllegalStateException(
"Must specify a Riak bucket in which to store the data if loading individual class files.");
}
if (verbose) {
System.out.println(String.format("Uploading to %s/%s", bucket, name));
}
// Load these bytes into Riak
riak.setAsBytes(bucket, name, bout.toByteArray());
} catch (FileNotFoundException e) {
System.err.println("Error reading class file: " + e.getMessage());
} catch (IOException e) {
System.err.println("Error reading class file: " + e.getMessage());
}
}
}
}
}
}

View File

@@ -1,186 +0,0 @@
/*
* Copyright (c) 2011 by J. Brisbin <jon@jbrisbin.com>
* Portions (c) 2011 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.util;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.keyvalue.riak.core.RiakTemplate;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
/**
* @author J. Brisbin <jon@jbrisbin.com>
*/
public class RiakClassLoader extends ClassLoader {
protected final Log log = LogFactory.getLog(getClass());
protected Set<String> buckets = new LinkedHashSet<String>();
protected RiakTemplate riakTemplate;
protected String defaultBucket = null;
public RiakClassLoader(ClassLoader classLoader, RiakTemplate riakTemplate) {
super(classLoader);
init(riakTemplate);
loadBucketsFromClassPath();
}
public RiakClassLoader(RiakTemplate riakTemplate) {
init(riakTemplate);
loadBucketsFromClassPath();
}
public Set<String> getBuckets() {
return buckets;
}
public void setBuckets(Set<String> buckets) {
this.buckets = buckets;
}
public RiakTemplate getRiakTemplate() {
return riakTemplate;
}
public void setRiakTemplate(RiakTemplate riakTemplate) {
this.riakTemplate = riakTemplate;
}
public String getDefaultBucket() {
return defaultBucket;
}
public void setDefaultBucket(String defaultBucket) {
this.defaultBucket = defaultBucket;
}
@Override
protected Class<?> findClass(String s) throws ClassNotFoundException {
Class<?> c;
try {
c = super.findClass(s);
if (log.isDebugEnabled()) {
log.debug(String.format("Found class '%s' locally defined.", s));
}
} catch (Throwable t) {
// Class not defined in this ClassLoader yet
}
Set<String> buckets = new LinkedHashSet<String>(this.buckets);
if (null != defaultBucket) {
buckets.add(defaultBucket);
}
for (String bucket : buckets) {
if (bucket.indexOf("/") < 0) {
try {
if (log.isDebugEnabled()) {
log.debug(String.format("Class '%s' not locally defined, trying Riak.", s));
}
byte[] buff = riakTemplate.getAsBytes(URLEncoder.encode(bucket, "UTF-8"), s);
c = defineClass(s, buff, 0, buff.length);
if (null != c) {
return c;
}
} catch (ClassFormatError ignored) {
} catch (UnsupportedEncodingException e) {
log.error(e.getMessage(), e);
}
}
}
// Nothing found
throw new ClassNotFoundException("Class not found: " + s);
}
protected void loadBucketsFromClassPath() {
String classPath = System.getProperty("java.class.path");
String pathSep = System.getProperty("path.separator", ":");
if (null != classPath) {
String[] paths = classPath.split(pathSep);
for (String p : paths) {
buckets.add(p);
}
}
}
protected void init(RiakTemplate riakTemplate) {
this.riakTemplate = riakTemplate;
RestTemplate tmpl = this.riakTemplate.getRestTemplate();
tmpl.getMessageConverters().add(0, new JavaSerializationMessageHandler());
tmpl.setErrorHandler(new Ignore404sErrorHandler());
}
private class JavaSerializationMessageHandler implements HttpMessageConverter {
public boolean canRead(Class clazz, MediaType mediaType) {
return MediaType.APPLICATION_OCTET_STREAM.equals(mediaType);
}
public boolean canWrite(Class clazz, MediaType mediaType) {
return null != clazz;
}
public List<MediaType> getSupportedMediaTypes() {
List<MediaType> types = new ArrayList<MediaType>(1);
types.add(MediaType.APPLICATION_OCTET_STREAM);
return types;
}
public Object read(java.lang.Class clazz, HttpInputMessage inputMessage) throws
IOException,
HttpMessageNotReadableException {
ObjectInputStream oin = new ObjectInputStream(inputMessage.getBody());
try {
Class<?> c = (Class<?>) oin.readObject();
if (log.isDebugEnabled()) {
log.debug("Loaded class: " + c);
}
return c;
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
public void write(Object o, MediaType contentType, HttpOutputMessage outputMessage) throws
IOException,
HttpMessageNotWritableException {
outputMessage.getHeaders().setContentType(MediaType.APPLICATION_OCTET_STREAM);
ObjectOutputStream oout = new ObjectOutputStream(outputMessage.getBody());
oout.writeObject(o);
oout.flush();
}
}
}

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<description>Example configuration to get you started.</description>
</beans>

View File

@@ -1,35 +0,0 @@
/*
* Copyright (c) 2011 by J. Brisbin <jon@jbrisbin.com>
* Portions (c) 2011 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;
/**
* @author J. Brisbin <jon@jbrisbin.com>
*/
public class ClassLoaderTest {
String name = "ClassLoaderTest";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@@ -1,87 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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 java.util.concurrent.Future
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/data/AsyncRiakTemplateTests.xml")
class AsyncRiakTemplateSpec extends Specification {
@Autowired
ApplicationContext appCtx
@Autowired
AsyncRiakTemplate riak
def "Test async setWithMetaData"() {
given:
def obj = [test: "value", integer: 12]
def success = false
def failure = false
def testValue = "bad value"
def callback = [
completed: { v ->
success = true
testValue = v.get().test
},
failed: { e ->
failure = true
}
] as AsyncKeyValueStoreOperation
when:
Future future = riak.setWithMetaData("test", "test", obj, null, null, callback)
println "Waiting for result: ${future.get()}"
then:
success && !failure
"value" == testValue
}
def "Test async getWithMetaData"() {
given:
def result = null
def callback = [
completed: { meta, v ->
println "got value: $meta $v"
result = v
},
failed: { e ->
println "got error: $e"
}
] as AsyncKeyValueStoreOperation
when:
riak.getWithMetaData("test", "test", Map, callback).get()
then:
null != result
}
}

View File

@@ -1,285 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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 java.util.concurrent.Future
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.ApplicationContext
import org.springframework.data.keyvalue.riak.groovy.RiakBuilder
import org.springframework.test.context.ContextConfiguration
import spock.lang.Specification
/**
* @author J. Brisbin <jon@jbrisbin.com>
*/
@ContextConfiguration(locations = "/org/springframework/data/AsyncRiakTemplateTests.xml")
class RiakBuilderSpec extends Specification {
@Autowired
ApplicationContext appCtx
@Autowired
AsyncRiakTemplate riakTemplate
def "Test builder set"() {
given:
def obj = [test: "value", integer: 12]
def riak = new RiakBuilder(riakTemplate)
def result = null
when:
riak {
set(bucket: "test", key: "test", qos: [dw: "all"], value: obj) {
completed(when: { it.integer == 12 }) { result = it.test }
completed { result = "otherwise" }
failed { it.printStackTrace() }
}
}
then:
"value" == result
}
def "Test builder get"() {
given:
def riak = new RiakBuilder(riakTemplate)
def result = null
when:
riak.get(bucket: "test", key: "test") {
completed(when: { it.integer == 12 }) { result = it.test }
completed { result = "otherwise" }
failed { it.printStackTrace() }
}
then:
null != result
"value" == result
}
def "Test builder async get"() {
given:
def riak = new RiakBuilder(riakTemplate)
def result = null
when:
def f = riak.get(bucket: "test", key: "test", wait: 0) {
completed(when: { it.integer == 12 }) { result = it.test }
completed { result = "otherwise" }
failed { it.printStackTrace() }
}
then:
f instanceof Future
null != f.get()
}
def "Test builder getAsType"() {
given:
def riak = new RiakBuilder(riakTemplate)
def result = null
when:
riak.getAsType(bucket: "test", key: "test", type: Map) {
completed(when: { it instanceof Map }) { result = it.test }
completed { result = "otherwise" }
failed { it.printStackTrace() }
}
then:
"value" == result
}
def "Test builder setAsBytes"() {
given:
def obj = "test bytes".bytes
def riak = new RiakBuilder(riakTemplate)
def result = null
when:
riak.setAsBytes(bucket: "test", key: "test", value: obj, qos: [dw: "all"]) {
completed { result = "success" }
failed { it.printStackTrace() }
}
then:
null != result
"success" == result
}
def "Test builder get with bytes"() {
given:
def riak = new RiakBuilder(riakTemplate)
when:
def result = riak.get(bucket: "test", key: "test") {
failed { it.printStackTrace() }
}
then:
null != result
"test bytes".bytes == result[0]
}
def "Test builder put"() {
given:
def obj = [test: "value", integer: 12]
def riak = new RiakBuilder(riakTemplate)
when:
def id = riak.put(bucket: "test", qos: [dw: "all"], value: obj) {
completed { v, meta -> meta.key }
failed { it.printStackTrace() }
}
then:
null != id
}
def "Test builder foreach"() {
given:
def riak = new RiakBuilder(riakTemplate)
def idCnt = 0
when:
riak.foreach(bucket: "test") {
completed { idCnt++ }
failed { it.printStackTrace() }
}
then:
idCnt > 0
}
def "Test builder batch operations"() {
given:
def riak = new RiakBuilder(riakTemplate)
def ids = []
when:
riak {
test {
put(value: [test: "value 1"])
put(value: [test: "value 2"])
put(value: [test: "value 3"])
foreach {
completed { v, meta -> ids << meta.key }
failed { it.printStackTrace() }
}
}
}
then:
null != ids
3 <= ids.size()
}
def "Test builder bucket as node"() {
given:
def riak = new RiakBuilder(riakTemplate)
def ids = []
when:
riak {
"test" {
put(value: [test: "value 1"])
put(value: [test: "value 2"])
put(value: [test: "value 3"])
foreach {
completed { v, meta -> ids << meta.key }
failed { it.printStackTrace() }
}
}
}
then:
null != ids
3 <= ids.size()
}
def "Test builder Map/Reduce"() {
given:
def riak = new RiakBuilder(riakTemplate)
when:
riak {
mapreduce {
inputs "test"
query {
map(arg: [test: "arg", alist: [1, 2, 3, 4]]) {
source "function(v, keyInfo, arg){ return [1]; }"
}
reduce {
source "function(v){ return Riak.reduceSum(v); }"
}
}
completed { it }
failed { it.printStackTrace() }
}
}
then:
null != riak.results
1 <= riak.results.size()
}
def "Test builder delete"() {
given:
def riak = new RiakBuilder(riakTemplate)
when:
riak {
"test" {
foreach {
completed { v, meta -> delete(key: meta.key) }
failed { deleted = false }
}
}
}
then:
!riak.results.find { !it }
}
}

View File

@@ -1,73 +0,0 @@
/*
* Copyright (c) 2011 by J. Brisbin <jon@jbrisbin.com>
* Portions (c) 2011 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.data.keyvalue.riak.util.RiakClassFileLoader
import org.springframework.data.keyvalue.riak.util.RiakClassLoader
import spock.lang.Shared
import spock.lang.Specification
/**
* @author J. Brisbin <jon@jbrisbin.com>
*/
class RiakClassLoaderSpec extends Specification {
@Shared RiakTemplate riakTemplate = new RiakTemplate()
def setupSpec() {
RiakQosParameters qos = new RiakQosParameters()
qos.durableWriteThreshold = "all"
riakTemplate.defaultQosParameters = qos
riakTemplate.ignoreNotFound = true
riakTemplate.afterPropertiesSet()
}
def "Test load class file into Riak"() {
when:
def args = [
"-c", "src/test/classes/org/springframework/data/keyvalue/riak/core/ClassLoaderTest.class",
"-b", "test",
"-k", "org.springframework.data.keyvalue.riak.core.ClassLoaderTest"
].toArray(new String[6])
RiakClassFileLoader.main(args)
then:
true
}
def "Test find class previously loaded into Riak"() {
given:
RiakClassLoader classLoader = new RiakClassLoader(riakTemplate)
classLoader.defaultBucket = "test"
when:
def clazz = Class.forName("org.springframework.data.keyvalue.riak.core.ClassLoaderTest", false, classLoader)
def inst = clazz?.newInstance()
then:
null != clazz
null != inst
inst.name == "ClassLoaderTest"
}
}

View File

@@ -1,283 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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.data.keyvalue.riak.mapreduce.JavascriptMapReduceOperation
import org.springframework.data.keyvalue.riak.mapreduce.MapReduceJob
import org.springframework.data.keyvalue.riak.mapreduce.RiakMapReducePhase
import org.springframework.data.keyvalue.riak.util.Ignore404sErrorHandler
import spock.lang.Shared
import spock.lang.Specification
/**
* @author J. Brisbin <jon@jbrisbin.com>
*/
class RiakKeyValueTemplateSpec extends Specification {
@Shared RiakKeyValueTemplate riak = new RiakKeyValueTemplate()
int run = 1
@Shared def riakBin = System.properties["bamboo.RIAK_BIN"] ?: "/usr/sbin/riak"
@Shared def p
def setupSpec() {
RiakQosParameters qos = new RiakQosParameters()
qos.setDurableWriteThreshold("all")
riak.setDefaultQosParameters(qos)
riak.getRestTemplate().setErrorHandler(new Ignore404sErrorHandler())
if (!riak.get("status", "")) {
p = "$riakBin start".execute()
p.waitFor()
shutdown = true
Thread.sleep(2000)
}
riak.getBucketSchema("test", true).keys.each {
riak.delete("test", it)
}
riak.getBucketSchema(TestObject.name, true).keys.each {
riak.delete("test", it)
}
}
def cleanupSpec() {
if (shutdown) {
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
}
}

View File

@@ -1,315 +0,0 @@
/*
* Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
* 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.data.keyvalue.riak.core.io.RiakFile
import org.springframework.data.keyvalue.riak.mapreduce.JavascriptMapReduceOperation
import org.springframework.data.keyvalue.riak.mapreduce.MapReduceJob
import org.springframework.data.keyvalue.riak.mapreduce.RiakMapReduceJob
import org.springframework.data.keyvalue.riak.mapreduce.RiakMapReducePhase
import spock.lang.Shared
import spock.lang.Specification
/**
* @author J. Brisbin <jon@jbrisbin.com>
*/
class RiakTemplateSpec extends Specification {
@Shared RiakTemplate riak = new RiakTemplate()
int run = 1
@Shared def riakBin = System.properties["bamboo.RIAK_BIN"] ?: "/usr/sbin/riak"
@Shared def p
@Shared def id
@Shared boolean shutdown = false
def setupSpec() {
RiakQosParameters qos = new RiakQosParameters()
qos.setDurableWriteThreshold("all")
riak.setDefaultQosParameters(qos)
riak.ignoreNotFound = true
if (!riak.get("status", "")) {
p = "$riakBin start".execute()
p.waitFor()
shutdown = true
Thread.sleep(2000)
}
riak.getBucketSchema("test", true).keys.each {
riak.delete("test", it)
}
riak.getBucketSchema(TestObject.name, true).keys.each {
riak.delete("test", it)
}
}
def cleanupSpec() {
if (shutdown) {
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 generating ID for object"() {
given:
def val = "value"
def objIn = [test: val, integer: 12]
when:
id = riak.put("test", objIn, null)
then:
null != id
}
def "Test custom object"() {
given:
TestObject objIn = new TestObject()
riak.set(TestObject.name, "test", objIn)
when:
TestObject objOut = riak.getAsType(TestObject.name, "test", TestObject)
then:
objOut.test == "value"
}
def "Test convert custom object from bytes"() {
given:
def qos = new RiakQosParameters()
qos.durableWriteThreshold = "all"
riak.setAsBytes(TestObject.name, "test", "{\"test\":\"string data\",\"integer\":1}".bytes, qos)
when:
def objOut = riak.getAsType(TestObject.name, "test", TestObject)
//riak.delete(TestObject.name, "test")
then:
objOut instanceof TestObject
objOut.test == "string data"
}
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("test", "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("test", "test")
then:
true == containsKey
}
def "Test linking"() {
given:
def qos = new RiakQosParameters()
qos.durableWriteThreshold = "all"
riak.set(TestObject.name, "test", new TestObject(), qos)
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 link walking as type"() {
when:
def val = riak.linkWalkAsType("test", "test", "test", Map)
then:
null != val
1 == val.size()
val.get(0) instanceof Map
}
def "Test getAndSet with Map"() {
given:
def i = run++
def newObj = [test: "value $i".toString(), integer: 12]
when:
def oldObj = riak.getAndSet("test", "test", newObj)
then:
"value" == oldObj.test
}
def "Test Map/Reduce returning Integer"() {
given:
MapReduceJob job = new RiakMapReduceJob(riak)
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 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"]).
addPhase(mapPhase).
addPhase(reducePhase)
println job.toJson()
when:
def result = riak.execute(job, Integer)
then:
2 == result
}
def "Test Map/Reduce returning List"() {
given:
MapReduceJob job = new RiakMapReduceJob(riak)
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 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"]).
addPhase(mapPhase).
addPhase(reducePhase)
when:
def result = riak.execute(job)
then:
1 == result.size()
2 == result[0]
}
def "Test RiakFile"() {
given:
def file = new RiakFile(riak, "test", "test")
when:
def exists = file.exists()
then:
exists
when:
def content = file.toURI().toURL().openConnection().getContent()
then:
null != content
}
def "Test delete key"() {
when:
def deleted = riak.deleteKeys("test:test", "${TestObject.name}:test", "test:$id")
then:
true == deleted
}
}

View File

@@ -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.data.keyvalue.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;
}
}

View File

@@ -1,14 +0,0 @@
log4j.rootCategory=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.category.org.apache.activemq=ERROR
log4j.category.org.springframework.batch=DEBUG
log4j.category.org.springframework.transaction=INFO
log4j.category.org.springframework.data=DEBUG
log4j.category.org.hibernate.SQL=DEBUG
# for debugging datasource initialization
# log4j.category.test.jdbc=DEBUG

View File

@@ -1,34 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
~ 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.
-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="classpath:/META-INF/spring/app-context.xml"/>
<bean id="qos" class="org.springframework.data.keyvalue.riak.core.RiakQosParameters"
p:durableWriteThreshold="all"
p:writeThreshold="all"/>
<bean id="riakTemplate" class="org.springframework.data.keyvalue.riak.core.AsyncRiakTemplate"
p:ignoreNotFound="true"
p:defaultQosParameters-ref="qos"/>
</beans>

View File

@@ -1,31 +0,0 @@
Bundle-SymbolicName: org.springframework.data.keyvalue.riak
Bundle-Name: Spring Data Riak Support
Bundle-Vendor: SpringSource
Bundle-ManifestVersion: 2
Import-Package:
sun.reflect;version="0";resolution:=optional
Import-Template:
org.springframework.beans.*;version="[3.0.0, 4.0.0)",
org.springframework.core.*;version="[3.0.0, 4.0.0)",
org.springframework.dao.*;version="[3.0.0, 4.0.0)",
org.springframework.http.*;version="[3.0.0, 4.0.0)",
org.springframework.http.client.*;version="[3.0.0, 4.0.0)",
org.springframework.web.*;version="[3.0.0, 4.0.0)",
org.springframework.web.client.*;version="[3.0.0, 4.0.0)",
org.springframework.util.*;version="[3.0.0, 4.0.0)",
org.springframework.data.core.*;version="[1.0.0, 2.0.0)",
org.springframework.data.core.*;version="[1.0.0, 2.0.0)",
org.springframework.data.*;version="[1.0.0, 2.0.0)",
org.springframework.data.persistence.*;version="[1.0.0, 2.0.0)",
org.springframework.data.document.*;version="[1.0.0, 2.0.0)",
org.aopalliance.*;version="[1.0.0, 2.0.0)";resolution:=optional,
org.apache.commons.logging.*;version="[1.1.1, 2.0.0)",
org.w3c.dom.*;version="0",
org.codehaus.jackson.*;version="[1.5.6, 1.5.6)",
org.codehaus.jackson.map.*;version="[1.5.6, 1.5.6)",
org.codehaus.groovy.runtime.*;version="[1.7.5, 2.0.0)",
groovy.lang.*;version="[1.7.5, 2.0.0)",
groovy.util.*;version="[1.7.5, 2.0.0)",
javax.activation.*;version="[1.1, 2.0)",
javax.mail.*;version="[1.4.0, 2.0.0)",
org.apache.commons.cli.*;version="[1.2, 2.0)",

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.data.keyvalue.redis;
package org.springframework.data.redis;
import org.springframework.dao.DataAccessResourceFailureException;

View File

@@ -14,16 +14,16 @@
* limitations under the License.
*/
package org.springframework.data.keyvalue.redis;
package org.springframework.data.redis;
import org.springframework.data.keyvalue.UncategorizedKeyvalueStoreException;
import org.springframework.dao.UncategorizedDataAccessException;
/**
* Exception thrown when we can't classify a Redis exception into one of Spring generic data access exceptions.
*
* @author Costin Leau
*/
public class RedisSystemException extends UncategorizedKeyvalueStoreException {
public class RedisSystemException extends UncategorizedDataAccessException {
public RedisSystemException(String msg, Throwable cause) {
super(msg, cause);

View File

@@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.keyvalue.redis.config;
package org.springframework.data.redis.config;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser;
import org.springframework.data.keyvalue.redis.support.collections.RedisCollectionFactoryBean;
import org.springframework.data.redis.support.collections.RedisCollectionFactoryBean;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.keyvalue.redis.config;
package org.springframework.data.redis.config;
import java.util.ArrayList;
import java.util.Collection;
@@ -24,11 +24,11 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.data.keyvalue.redis.listener.ChannelTopic;
import org.springframework.data.keyvalue.redis.listener.PatternTopic;
import org.springframework.data.keyvalue.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.keyvalue.redis.listener.Topic;
import org.springframework.data.keyvalue.redis.listener.adapter.MessageListenerAdapter;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.Topic;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.keyvalue.redis.config;
package org.springframework.data.redis.config;
import org.springframework.beans.factory.xml.NamespaceHandler;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.data.keyvalue.redis.connection;
package org.springframework.data.redis.connection;
import java.util.EnumSet;
import java.util.Map;

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.keyvalue.redis.connection;
package org.springframework.data.redis.connection;
/**

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.keyvalue.redis.connection;
package org.springframework.data.redis.connection;
import java.util.ArrayList;
import java.util.List;

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.keyvalue.redis.connection;
package org.springframework.data.redis.connection;
import java.util.Collection;
import java.util.LinkedHashMap;
@@ -23,10 +23,10 @@ import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.springframework.data.keyvalue.redis.RedisSystemException;
import org.springframework.data.keyvalue.redis.serializer.RedisSerializer;
import org.springframework.data.keyvalue.redis.serializer.SerializationUtils;
import org.springframework.data.keyvalue.redis.serializer.StringRedisSerializer;
import org.springframework.data.redis.RedisSystemException;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationUtils;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.Assert;
/**

View File

@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.keyvalue.redis.connection;
package org.springframework.data.redis.connection;
import org.springframework.data.keyvalue.redis.connection.RedisZSetCommands.Tuple;
import org.springframework.data.keyvalue.redis.connection.StringRedisConnection.StringTuple;
import org.springframework.data.redis.connection.RedisZSetCommands.Tuple;
import org.springframework.data.redis.connection.StringRedisConnection.StringTuple;
/**
* Default implementation for {@link StringTuple} interface.

Some files were not shown because too many files have changed in this diff Show More