diff --git a/spring-integration-voldemort/README.md b/spring-integration-voldemort/README.md new file mode 100755 index 0000000..965caee --- /dev/null +++ b/spring-integration-voldemort/README.md @@ -0,0 +1,56 @@ +Spring Integration Voldemort Adapter +==================================== + +The Voldemort extension for Spring Integration (SI) project includes inbound +and outbound channel adapters. + +Inbound channel adapter: +----------------------------------------------------------------------------- +Inbound channel adapter is used to retrieve data out of Voldemort database +and transfer objects into Spring Integration's channel. Component expects +user to provide Voldemort store client, message converter and desired object's +key. + +### Example: +~~~~~xml + + ... + + + + ... + +~~~~~ + +For more implementation details please review documentation and integration +test cases. + +Outbound channel adapter: +----------------------------------------------------------------------------- +Outbound channel adapter is used to insert data into Voldemort database +from Spring Integration's channel. Component expects user to provide +Voldemort store client and message converter. + +### Example: +~~~~~xml + + ... + + ... + +~~~~~ + +For more implementation details please review documentation and integration +test cases. + +Build +----------------------------------------------------------------------------- +For build instructions visit [Spring Integration on GitHub](https://github.com/SpringSource/spring-integration). \ No newline at end of file diff --git a/spring-integration-voldemort/build.gradle b/spring-integration-voldemort/build.gradle new file mode 100755 index 0000000..61618f8 --- /dev/null +++ b/spring-integration-voldemort/build.gradle @@ -0,0 +1,266 @@ +description = 'Spring Integration Voldemort Adapter' + +buildscript { + repositories { + maven { url 'https://repo.springsource.org/plugins-snapshot' } + } + dependencies { + classpath 'org.springframework.build.gradle:docbook-reference-plugin:0.1.5' + } +} + +apply plugin: 'java' +apply from: "${rootProject.projectDir}/publish-maven.gradle" +apply plugin: 'eclipse' +apply plugin: 'idea' + +group = 'org.springframework.integration.voldemort' + +repositories { + maven { url 'http://repo.springsource.org/libs-milestone' } + maven { url 'http://repo.springsource.org/plugins-release' } +} + +sourceCompatibility=1.6 +targetCompatibility=1.6 + +ext { + junitVersion = '4.10' + log4jVersion = '1.2.12' + mockitoVersion = '1.9.0' + springVersion = '3.1.3.RELEASE' + springIntegrationVersion = '2.2.0.RELEASE' + voldemortVersion = '0.96' + + shortName = 'voldemort' +} + +eclipse { + project { + natures += 'org.springframework.ide.eclipse.core.springnature' + } +} + +sourceSets { + test { + resources { + srcDirs = ['src/test/resources', 'src/test/java'] + } + } +} + +// See http://www.gradle.org/docs/current/userguide/dependency_management.html#sub:configurations +// and http://www.gradle.org/docs/current/dsl/org.gradle.api.artifacts.ConfigurationContainer.html +configurations { + jacoco //Configuration Group used by Sonar to provide Code Coverage using JaCoCo +} + +dependencies { + compile "org.springframework.integration:spring-integration-core:$springIntegrationVersion" + compile "voldemort:voldemort:$voldemortVersion" + testCompile "org.springframework.integration:spring-integration-test:$springIntegrationVersion" + testCompile "junit:junit-dep:$junitVersion" + testCompile "log4j:log4j:$log4jVersion" + testCompile "org.mockito:mockito-all:$mockitoVersion" + testCompile "org.springframework:spring-test:$springVersion" + jacoco group: "org.jacoco", name: "org.jacoco.agent", version: "0.5.6.201201232323", classifier: "runtime" +} + + +// enable all compiler warnings; individual projects may customize further +ext.xLintArg = '-Xlint:all' +[compileJava, compileTestJava]*.options*.compilerArgs = [xLintArg] + +test { + // suppress all console output during testing unless running `gradle -i` + logging.captureStandardOutput(LogLevel.INFO) + // JVM settings from http://www.project-voldemort.com/voldemort/configuration.html + jvmArgs "-XX:+UseConcMarkSweepGC", "-XX:+UseParNewGC", "-javaagent:${configurations.jacoco.asPath}=destfile=${buildDir}/jacoco.exec,includes=*" +} + +task sourcesJar(type: Jar) { + classifier = 'sources' + from sourceSets.main.allJava +} + +task javadocJar(type: Jar) { + classifier = 'javadoc' + from javadoc +} + +artifacts { + archives sourcesJar + archives javadocJar +} + +apply plugin: 'docbook-reference' + +reference { + sourceDir = file('src/reference/docbook') +} + +apply plugin: 'sonar' + +sonar { + + if (rootProject.hasProperty('sonarHostUrl')) { + server.url = rootProject.sonarHostUrl + } + + database { + if (rootProject.hasProperty('sonarJdbcUrl')) { + url = rootProject.sonarJdbcUrl + } + if (rootProject.hasProperty('sonarJdbcDriver')) { + driverClassName = rootProject.sonarJdbcDriver + } + if (rootProject.hasProperty('sonarJdbcUsername')) { + username = rootProject.sonarJdbcUsername + } + if (rootProject.hasProperty('sonarJdbcPassword')) { + password = rootProject.sonarJdbcPassword + } + } + + project { + dynamicAnalysis = "reuseReports" + withProjectProperties { props -> + props["sonar.core.codeCoveragePlugin"] = "jacoco" + props["sonar.jacoco.reportPath"] = "${buildDir.name}/jacoco.exec" + } + } + + logger.info("Sonar parameters used: server.url='${server.url}'; database.url='${database.url}'; database.driverClassName='${database.driverClassName}'; database.username='${database.username}'") +} + +task api(type: Javadoc) { + group = 'Documentation' + description = 'Generates the Javadoc API documentation.' + title = "${rootProject.description} ${version} API" + options.memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PROTECTED + options.author = true + options.header = rootProject.description + options.overview = 'src/api/overview.html' + + source = sourceSets.main.allJava + classpath = project.sourceSets.main.compileClasspath + destinationDir = new File(buildDir, "api") +} + +task schemaZip(type: Zip) { + group = 'Distribution' + classifier = 'schema' + description = "Builds -${classifier} archive containing all " + + "XSDs for deployment at static.springframework.org/schema." + + def Properties schemas = new Properties(); + + project.sourceSets.main.resources.find { + it.path.endsWith('META-INF/spring.schemas') + }?.withInputStream { schemas.load(it) } + + for (def key : schemas.keySet()) { + File xsdFile = project.sourceSets.main.resources.find { + it.path.endsWith(schemas.get(key)) + } + assert xsdFile != null + into ("integration/${shortName}") { + from xsdFile.path + } + } + +} + +task docsZip(type: Zip) { + group = 'Distribution' + classifier = 'docs' + description = "Builds -${classifier} archive containing api and reference " + + "for deployment at static.springframework.org/spring-integration/docs." + + from('src/dist') { + include 'changelog.txt' + } + + from (api) { + into 'api' + } + + from (reference) { + into 'reference' + } +} + +task distZip(type: Zip, dependsOn: [docsZip, schemaZip]) { + group = 'Distribution' + classifier = 'dist' + description = "Builds -${classifier} archive, containing all jars and docs, " + + "suitable for community download page." + + ext.baseDir = "${project.name}-${project.version}"; + + from('src/dist') { + include 'readme.txt' + include 'license.txt' + include 'notice.txt' + into "${baseDir}" + } + + from(zipTree(docsZip.archivePath)) { + into "${baseDir}/docs" + } + + from(zipTree(schemaZip.archivePath)) { + into "${baseDir}/schema" + } + + into ("${baseDir}/libs") { + from project.jar + from project.sourcesJar + from project.javadocJar + } +} + +// Create an optional "with dependencies" distribution. +// Not published by default; only for use when building from source. +task depsZip(type: Zip, dependsOn: distZip) { zipTask -> + group = 'Distribution' + classifier = 'dist-with-deps' + description = "Builds -${classifier} archive, containing everything " + + "in the -${distZip.classifier} archive plus all dependencies." + + from zipTree(distZip.archivePath) + + gradle.taskGraph.whenReady { taskGraph -> + if (taskGraph.hasTask(":${zipTask.name}")) { + def projectName = rootProject.name + def artifacts = new HashSet() + + rootProject.configurations.runtime.resolvedConfiguration.resolvedArtifacts.each { artifact -> + def dependency = artifact.moduleVersion.id + if (!projectName.equals(dependency.name)) { + artifacts << artifact.file + } + } + + zipTask.from(artifacts) { + into "${distZip.baseDir}/deps" + } + } + } +} + +artifacts { + archives distZip + archives docsZip + archives schemaZip +} + +task dist(dependsOn: assemble) { + group = 'Distribution' + description = 'Builds -dist, -docs and -schema distribution archives.' +} + +task wrapper(type: Wrapper) { + description = 'Generates gradlew[.bat] scripts' + gradleVersion = '1.3' +} diff --git a/spring-integration-voldemort/gradle.properties b/spring-integration-voldemort/gradle.properties new file mode 100755 index 0000000..bebfcbc --- /dev/null +++ b/spring-integration-voldemort/gradle.properties @@ -0,0 +1 @@ +version=1.0.0.BUILD-SNAPSHOT diff --git a/spring-integration-voldemort/gradle/wrapper/gradle-wrapper.jar b/spring-integration-voldemort/gradle/wrapper/gradle-wrapper.jar new file mode 100755 index 0000000..81dcde6 Binary files /dev/null and b/spring-integration-voldemort/gradle/wrapper/gradle-wrapper.jar differ diff --git a/spring-integration-voldemort/gradle/wrapper/gradle-wrapper.properties b/spring-integration-voldemort/gradle/wrapper/gradle-wrapper.properties new file mode 100755 index 0000000..66b16f7 --- /dev/null +++ b/spring-integration-voldemort/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Sep 05 13:34:36 EDT 2012 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=http\://services.gradle.org/distributions/gradle-1.1-bin.zip diff --git a/spring-integration-voldemort/gradlew b/spring-integration-voldemort/gradlew new file mode 100755 index 0000000..cf12650 --- /dev/null +++ b/spring-integration-voldemort/gradlew @@ -0,0 +1,164 @@ +#!/bin/bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" +APP_HOME="`pwd -P`" +cd "$SAVED" + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query businessSystem maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/spring-integration-voldemort/gradlew.bat b/spring-integration-voldemort/gradlew.bat new file mode 100755 index 0000000..8a0b282 --- /dev/null +++ b/spring-integration-voldemort/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/spring-integration-voldemort/publish-maven.gradle b/spring-integration-voldemort/publish-maven.gradle new file mode 100755 index 0000000..0ba656a --- /dev/null +++ b/spring-integration-voldemort/publish-maven.gradle @@ -0,0 +1,61 @@ +apply plugin: 'maven' + +ext.optionalDeps = [] +ext.providedDeps = [] + +ext.optional = { optionalDeps << it } +ext.provided = { providedDeps << it } + +install { + repositories.mavenInstaller { + customizePom(pom, project) + } +} + +def customizePom(pom, gradleProject) { + pom.whenConfigured { generatedPom -> + // respect 'optional' and 'provided' dependencies + gradleProject.optionalDeps.each { dep -> + generatedPom.dependencies.find { it.artifactId == dep.name }?.optional = true + } + gradleProject.providedDeps.each { dep -> + generatedPom.dependencies.find { it.artifactId == dep.name }?.scope = 'provided' + } + + // eliminate test-scoped dependencies (no need in maven central poms) + generatedPom.dependencies.removeAll { dep -> + dep.scope == 'test' + } + + // add all items necessary for maven central publication + generatedPom.project { + name = gradleProject.description + description = gradleProject.description + url = 'https://github.com/SpringSource/spring-integration-extensions' + organization { + name = 'SpringSource' + url = 'http://springsource.org' + } + licenses { + license { + name 'The Apache Software License, Version 2.0' + url 'http://www.apache.org/licenses/LICENSE-2.0.txt' + distribution 'repo' + } + } + scm { + url = 'https://github.com/SpringSource/spring-integration-extensions' + connection = 'scm:git:git://github.com/SpringSource/spring-integration-extensions' + developerConnection = 'scm:git:git://github.com/SpringSource/spring-integration-extensions' + } + + developers { + developer { + id = 'lukasz-antoniak' + name = 'Lukasz Antoniak' + email = 'Lukasz.Antoniak@gmail.com' + } + } + } + } +} diff --git a/spring-integration-voldemort/src/api/overview.html b/spring-integration-voldemort/src/api/overview.html new file mode 100755 index 0000000..fb0198b --- /dev/null +++ b/spring-integration-voldemort/src/api/overview.html @@ -0,0 +1,22 @@ + + +This document is the API specification for Spring Integration +
+
+

+ For further API reference and developer documentation, see the + Spring + Integration reference documentation. + That documentation contains more detailed, developer-targeted + descriptions, with conceptual overviews, definitions of terms, + workarounds, and working code examples. +

+ +

+ If you are interested in commercial training, consultancy, and + support for Spring Integration, please visit + http://www.springsource.com +

+
+ + diff --git a/spring-integration-voldemort/src/dist/changelog.txt b/spring-integration-voldemort/src/dist/changelog.txt new file mode 100755 index 0000000..5a45c4e --- /dev/null +++ b/spring-integration-voldemort/src/dist/changelog.txt @@ -0,0 +1,10 @@ +Spring Integration Voldemort Adapter Changelog +============================================== + +Changes in version 1.0.0 (13 Jan 2013) +------------------------------------- + +** New Feature + * Voldemort Inbound Adapter + * Voldemort Outbound Adapter + * Voldemort Message Store \ No newline at end of file diff --git a/spring-integration-voldemort/src/dist/license.txt b/spring-integration-voldemort/src/dist/license.txt new file mode 100755 index 0000000..261eeb9 --- /dev/null +++ b/spring-integration-voldemort/src/dist/license.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/spring-integration-voldemort/src/dist/notice.txt b/spring-integration-voldemort/src/dist/notice.txt new file mode 100755 index 0000000..f62045a --- /dev/null +++ b/spring-integration-voldemort/src/dist/notice.txt @@ -0,0 +1,21 @@ + ======================================================================== + == NOTICE file corresponding to section 4 d of the Apache License, == + == Version 2.0, in this case for the Spring Integration distribution. == + ======================================================================== + + This product includes software developed by + the Apache Software Foundation (http://www.apache.org). + + The end-user documentation included with a redistribution, if any, + must include the following acknowledgement: + + "This product includes software developed by the Spring Framework + Project (http://www.springframework.org)." + + Alternatively, this acknowledgement may appear in the software itself, + if and wherever such third-party acknowledgements normally appear. + + The names "Spring", "Spring Framework", and "Spring Integration" must + not be used to endorse or promote products derived from this software + without prior written permission. For written permission, please contact + enquiries@springsource.com. diff --git a/spring-integration-voldemort/src/dist/readme.txt b/spring-integration-voldemort/src/dist/readme.txt new file mode 100755 index 0000000..edccc0c --- /dev/null +++ b/spring-integration-voldemort/src/dist/readme.txt @@ -0,0 +1,13 @@ +Spring Integration Voldemort Adapter +------------------------------------ + +To find out what has changed since any earlier releases, see 'changelog.txt'. + +Please consult the documentation located within the 'docs/reference' directory +of this release and also visit the official Spring Integration home at +http://www.springsource.org/spring-integration + +There you will find links to the forum, issue tracker, and several other resources. + +See https://github.com/SpringSource/spring-integration#readme for additional +information including instructions on building from source. diff --git a/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/config/xml/VoldemortInboundChannelAdapterParser.java b/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/config/xml/VoldemortInboundChannelAdapterParser.java new file mode 100644 index 0000000..aa4fc98 --- /dev/null +++ b/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/config/xml/VoldemortInboundChannelAdapterParser.java @@ -0,0 +1,53 @@ +/* + * Copyright 2002-2013 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.integration.voldemort.config.xml; + +import org.springframework.beans.BeanMetadataElement; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.config.xml.AbstractPollingInboundChannelAdapterParser; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.springframework.integration.voldemort.inbound.VoldemortMessageSource; +import org.w3c.dom.Element; + +/** + * Parses Voldemort inbound adapter XML definition. + * + * @author Lukasz Antoniak + * @since 1.0 + */ +public class VoldemortInboundChannelAdapterParser extends AbstractPollingInboundChannelAdapterParser { + /** + * Produces "int-voldemort:inbound-channel-adapter" bean definition. + *

+ * {@inheritDoc} + */ + @Override + protected BeanMetadataElement parseSource(Element element, ParserContext parserContext) { + final BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( VoldemortMessageSource.class ); + VoldemortParserUtils.processCommonAttributes( element, builder ); + final RootBeanDefinition queryExpressionDef = + IntegrationNamespaceUtils.createExpressionDefinitionFromValueOrExpression( + VoldemortParserUtils.SEARCH_KEY, VoldemortParserUtils.SEARCH_KEY_EXPRESSION, + parserContext, element, true + ); + builder.addPropertyValue( VoldemortParserUtils.KEY_EXPRESSION_PROPERTY, queryExpressionDef ); + IntegrationNamespaceUtils.setValueIfAttributeDefined( builder, element, VoldemortParserUtils.DELETE_AFTER_POLL ); + IntegrationNamespaceUtils.setValueIfAttributeDefined( builder, element, VoldemortParserUtils.EXTRACT_VALUE ); + return builder.getBeanDefinition(); + } +} diff --git a/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/config/xml/VoldemortNamespaceHandler.java b/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/config/xml/VoldemortNamespaceHandler.java new file mode 100755 index 0000000..85ab0fe --- /dev/null +++ b/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/config/xml/VoldemortNamespaceHandler.java @@ -0,0 +1,31 @@ +/* + * Copyright 2002-2013 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.integration.voldemort.config.xml; + +import org.springframework.integration.config.xml.AbstractIntegrationNamespaceHandler; + +/** + * The handler for Voldemort extension namespace. + * + * @author Lukasz Antoniak + * @since 1.0 + */ +public class VoldemortNamespaceHandler extends AbstractIntegrationNamespaceHandler { + public void init() { + registerBeanDefinitionParser( "inbound-channel-adapter", new VoldemortInboundChannelAdapterParser() ); + registerBeanDefinitionParser( "outbound-channel-adapter", new VoldemortOutboundChannelAdapterParser() ); + } +} diff --git a/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/config/xml/VoldemortOutboundChannelAdapterParser.java b/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/config/xml/VoldemortOutboundChannelAdapterParser.java new file mode 100755 index 0000000..3ee51aa --- /dev/null +++ b/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/config/xml/VoldemortOutboundChannelAdapterParser.java @@ -0,0 +1,65 @@ +/* + * Copyright 2002-2013 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.integration.voldemort.config.xml; + +import org.springframework.beans.factory.config.TypedStringValue; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.config.ExpressionFactoryBean; +import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.springframework.integration.voldemort.outbound.VoldemortStoringMessageHandler; +import org.w3c.dom.Element; + +/** + * Parses Voldemort outbound adapter XML definition. + * + * @author Lukasz Antoniak + * @since 1.0 + */ +public class VoldemortOutboundChannelAdapterParser extends AbstractOutboundChannelAdapterParser { + /** + * Produces "int-voldemort:outbound-channel-adapter" bean definition. + *

+ * {@inheritDoc} + */ + @Override + protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) { + final BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( VoldemortStoringMessageHandler.class ); + VoldemortParserUtils.processCommonAttributes( element, builder ); + final boolean hasKey = element.hasAttribute( VoldemortParserUtils.STORE_KEY ); + final boolean hasKeyExpression = element.hasAttribute( VoldemortParserUtils.STORE_KEY_EXPRESSION ); + if ( hasKey && hasKeyExpression ) { + parserContext.getReaderContext().error( "At most one of '" + VoldemortParserUtils.STORE_KEY + + "' or '" + VoldemortParserUtils.STORE_KEY_EXPRESSION + "' is allowed.", element ); + } + if ( hasKey ) { + builder.addPropertyValue( + VoldemortParserUtils.KEY_PROPERTY, + new TypedStringValue( element.getAttribute( VoldemortParserUtils.STORE_KEY ) ) + ); + } + if ( hasKeyExpression ) { + RootBeanDefinition expressionDef = new RootBeanDefinition( ExpressionFactoryBean.class ); + expressionDef.getConstructorArgumentValues().addGenericArgumentValue( element.getAttribute( VoldemortParserUtils.STORE_KEY_EXPRESSION ) ); + builder.addPropertyValue( VoldemortParserUtils.KEY_EXPRESSION_PROPERTY, expressionDef ); + } + IntegrationNamespaceUtils.setValueIfAttributeDefined( builder, element, VoldemortParserUtils.PERSIST_MODE ); + return builder.getBeanDefinition(); + } +} diff --git a/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/config/xml/VoldemortParserUtils.java b/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/config/xml/VoldemortParserUtils.java new file mode 100755 index 0000000..1762115 --- /dev/null +++ b/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/config/xml/VoldemortParserUtils.java @@ -0,0 +1,47 @@ +/* + * Copyright 2002-2013 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.integration.voldemort.config.xml; + +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.w3c.dom.Element; + +/** + * Contains various utility methods for parsing Voldemort Adapter specific namesspace elements. + * + * @author Lukasz Antoniak + * @since 1.0 + */ +abstract class VoldemortParserUtils { + static final String STORE_CLIENT = "store-client"; + static final String PERSIST_MODE = "persist-mode"; + static final String SEARCH_KEY = "search-key"; + static final String SEARCH_KEY_EXPRESSION = "search-key-expression"; + static final String STORE_KEY = "store-key"; + static final String STORE_KEY_EXPRESSION = "store-key-expression"; + static final String DELETE_AFTER_POLL = "delete-after-poll"; + static final String EXTRACT_VALUE = "extract-value"; + + static final String KEY_PROPERTY = "key"; + static final String KEY_EXPRESSION_PROPERTY = "keyExpression"; + + /** + * Handles "store-client" and "message-converter" attributes. + */ + static void processCommonAttributes(Element element, BeanDefinitionBuilder builder) { + final String storeClient = element.getAttribute( VoldemortParserUtils.STORE_CLIENT ); + builder.addConstructorArgReference( storeClient ); + } +} diff --git a/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/config/xml/package-info.java b/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/config/xml/package-info.java new file mode 100755 index 0000000..8c9202d --- /dev/null +++ b/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/config/xml/package-info.java @@ -0,0 +1,4 @@ +/** + * Provides parser classes to manage XML namespace for the Voldemort components. + */ +package org.springframework.integration.voldemort.config.xml; diff --git a/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/inbound/VoldemortMessageSource.java b/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/inbound/VoldemortMessageSource.java new file mode 100644 index 0000000..d6524e3 --- /dev/null +++ b/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/inbound/VoldemortMessageSource.java @@ -0,0 +1,101 @@ +/* + * Copyright 2002-2013 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.integration.voldemort.inbound; + +import org.springframework.expression.Expression; +import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.springframework.integration.Message; +import org.springframework.integration.context.IntegrationObjectSupport; +import org.springframework.integration.core.MessageSource; +import org.springframework.integration.expression.ExpressionUtils; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.integration.voldemort.support.VoldemortHeaders; +import voldemort.client.StoreClient; +import voldemort.versioning.Versioned; + +/** + * Voldemort polling inbound adapter implementation. Regularly tries to retrieve object with a given key. + * + * @author Lukasz Antoniak + * @since 1.0 + */ +public class VoldemortMessageSource extends IntegrationObjectSupport implements MessageSource { + private final StoreClient client; + + /** + * Key expression which will be evaluated on every call to the {@link #receive()} method. + */ + private volatile Expression keyExpression; + private volatile StandardEvaluationContext evaluationContext; + private volatile boolean extractValue = true; + private volatile boolean deleteAfterPoll = false; + + /** + * Creates new message source. + * + * @param client Voldemort store client. + */ + public VoldemortMessageSource(StoreClient client) { + this.client = client; + } + + @Override + protected void onInit() throws Exception { + super.onInit(); + if ( getBeanFactory() != null ) { + evaluationContext = ExpressionUtils.createStandardEvaluationContext( getBeanFactory() ); + } + else { + evaluationContext = ExpressionUtils.createStandardEvaluationContext(); + } + } + + @Override + @SuppressWarnings("unchecked") + public Message receive() { + final Object key = keyExpression.getValue( evaluationContext, Object.class ); + final Versioned value = client.get( key ); + if ( value != null ) { + if ( deleteAfterPoll ) { + client.delete( key ); + } + return MessageBuilder.withPayload( extractValue ? value.getValue() : value ) + .setHeader( VoldemortHeaders.KEY, key ).build(); + } + return null; + } + + @Override + public String getComponentType() { + return "voldemort:inbound-channel-adapter"; + } + + public void setDeleteAfterPoll(boolean deleteAfterPoll) { + this.deleteAfterPoll = deleteAfterPoll; + } + + public Expression getKeyExpression() { + return keyExpression; + } + + public void setKeyExpression(Expression keyExpression) { + this.keyExpression = keyExpression; + } + + public void setExtractValue(boolean extractValue) { + this.extractValue = extractValue; + } +} diff --git a/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/inbound/package-info.java b/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/inbound/package-info.java new file mode 100755 index 0000000..5c18a3c --- /dev/null +++ b/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/inbound/package-info.java @@ -0,0 +1,4 @@ +/** + * Provides inbound Spring Integration Voldemort components. + */ +package org.springframework.integration.voldemort.inbound; diff --git a/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/outbound/VoldemortStoringMessageHandler.java b/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/outbound/VoldemortStoringMessageHandler.java new file mode 100755 index 0000000..8704746 --- /dev/null +++ b/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/outbound/VoldemortStoringMessageHandler.java @@ -0,0 +1,112 @@ +/* + * Copyright 2002-2013 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.integration.voldemort.outbound; + +import org.springframework.expression.Expression; +import org.springframework.expression.common.LiteralExpression; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.springframework.integration.Message; +import org.springframework.integration.expression.ExpressionUtils; +import org.springframework.integration.handler.AbstractMessageHandler; +import org.springframework.integration.voldemort.support.PersistMode; +import org.springframework.integration.voldemort.support.VoldemortHeaders; +import voldemort.client.StoreClient; + +/** + * Voldemort outbound adapter implementation. + * + * @author Lukasz Antoniak + * @since 1.0 + */ +public class VoldemortStoringMessageHandler extends AbstractMessageHandler { + private final StoreClient client; + + private volatile StandardEvaluationContext evaluationContext; + private volatile Expression keyExpression = new SpelExpressionParser().parseExpression( "headers." + VoldemortHeaders.KEY ); + + private volatile PersistMode persistMode = PersistMode.PUT; + + /** + * Creates new message sender. + * + * @param client Voldemort store client. + */ + public VoldemortStoringMessageHandler(StoreClient client) { + this.client = client; + } + + @Override + protected void onInit() throws Exception { + super.onInit(); + if ( getBeanFactory() != null ) { + evaluationContext = ExpressionUtils.createStandardEvaluationContext( getBeanFactory() ); + } + else { + evaluationContext = ExpressionUtils.createStandardEvaluationContext(); + } + } + + @Override + @SuppressWarnings("unchecked") + protected void handleMessageInternal(Message message) throws Exception { + final Object key = keyExpression.getValue( evaluationContext, message, Object.class ); + switch ( determinePersistMode( message ) ) { + case PUT: + client.put( key, message.getPayload() ); + break; + case DELETE: + client.delete( key ); + break; + } + } + + /** + * Computes desired persist mode for a given message. Default output adapter's configuration + * can be overridden with {@link VoldemortHeaders#PERSIST_MODE} message header which supports + * direct or text representation of {@link PersistMode} enumeration. + * + * @param message Spring Integration message. + * @return Persist mode. + */ + private PersistMode determinePersistMode(Message message) { + final Object confValue = message.getHeaders().get( VoldemortHeaders.PERSIST_MODE ); + if ( confValue instanceof PersistMode ) { + return (PersistMode) confValue; + } + else if ( confValue instanceof String ) { + return PersistMode.valueOf( (String) confValue ); + } + return persistMode; + } + + @Override + public String getComponentType() { + return "voldemort:outbound-channel-adapter"; + } + + public void setKey(String key) { + setKeyExpression( new LiteralExpression( key ) ); + } + + public void setKeyExpression(Expression keyExpression) { + this.keyExpression = keyExpression; + } + + public void setPersistMode(PersistMode persistMode) { + this.persistMode = persistMode; + } +} diff --git a/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/outbound/package-info.java b/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/outbound/package-info.java new file mode 100755 index 0000000..a77b939 --- /dev/null +++ b/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/outbound/package-info.java @@ -0,0 +1,4 @@ +/** + * Provides Spring Integration Voldemort components for doing outbound operations. + */ +package org.springframework.integration.voldemort.outbound; diff --git a/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/package-info.java b/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/package-info.java new file mode 100755 index 0000000..c4eebeb --- /dev/null +++ b/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/package-info.java @@ -0,0 +1,4 @@ +/** + * Root package of the Voldemort Module. + */ +package org.springframework.integration.voldemort; diff --git a/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/store/VoldemortMessageStore.java b/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/store/VoldemortMessageStore.java new file mode 100644 index 0000000..6caf424 --- /dev/null +++ b/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/store/VoldemortMessageStore.java @@ -0,0 +1,234 @@ +/* + * Copyright 2002-2013 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.integration.voldemort.store; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.locks.Lock; + +import org.springframework.beans.factory.InitializingBean; +import org.springframework.integration.store.AbstractKeyValueMessageStore; +import org.springframework.integration.store.MessageGroupStore; +import org.springframework.integration.store.MessageStore; +import org.springframework.integration.util.DefaultLockRegistry; +import org.springframework.integration.util.LockRegistry; +import org.springframework.integration.util.UUIDConverter; +import voldemort.client.StoreClient; +import voldemort.client.UpdateAction; +import voldemort.serialization.SerializationException; +import voldemort.versioning.Versioned; + +/** + * Voldemort implementation of the key-value style {@link MessageStore} and {@link MessageGroupStore}. + * Implementation note: message identifiers are persisted as {@link String}s. + * + * @author Lukasz Antoniak + * @since 1.0 + */ +@SuppressWarnings("unchecked") +public class VoldemortMessageStore extends AbstractKeyValueMessageStore implements InitializingBean { + /** + * Key under which message store tracks all currently saved message identifiers. + */ + protected final static String MESSAGE_KEY_LIST = "MESSAGE_KEY_LIST"; + + /** + * Key under which message store tracks all currently saved message group identifiers. + */ + protected final static String MESSAGE_GROUP_KEY_LIST = "MESSAGE_GROUP_KEY_LIST"; + + private final static LockRegistry LOCK_REGISTRY = new DefaultLockRegistry(); + + private final StoreClient client; + + public VoldemortMessageStore(StoreClient client) { + this.client = client; + } + + @Override + public void afterPropertiesSet() throws Exception { + // Initialize empty set of message and message group identifiers if needed. + synchronized ( getClass() ) { + if ( client.get( MESSAGE_KEY_LIST ) == null ) { + client.put( MESSAGE_KEY_LIST, new HashSet() ); + } + if ( client.get( MESSAGE_GROUP_KEY_LIST ) == null ) { + client.put( MESSAGE_GROUP_KEY_LIST, new HashSet() ); + } + } + } + + @Override + protected Object doRetrieve(Object id) { + final Versioned version = client.get( id ); + if ( version != null ) { + return version.getValue(); + } + return null; + } + + @Override + protected void doStore(final Object id, final Object objectToStore) { + final Lock messageLock = doLock( id ); + try { + client.put( id, objectToStore ); + // Keeping track of stored message identifiers for efficient implementation of #doListKeys(String) method. + // Voldemort does not provide API to list all keys matching specified pattern. + final String keyListKey = computeKeyListKey( id ); + final Lock keyListLock = doLock( keyListKey ); + try { + client.applyUpdate( new AddKeyUpdateAction( id, keyListKey ) ); + } + finally { + keyListLock.unlock(); + } + } + catch ( SerializationException e ) { + throw new IllegalArgumentException( "Voldemort failed to serialize message with id: " + id + "." ); + } + finally { + messageLock.unlock(); + } + } + + @Override + protected Object doRemove(final Object id) { + final Object message = doRetrieve( id ); + final Lock messageLock = doLock( id ); + try { + client.delete( id ); + // Keeping track of stored message identifiers for efficient implementation of #doListKeys(String) method. + // Voldemort does not provide API to list all keys matching specified pattern. + final String keyListKey = computeKeyListKey( id ); + final Lock keyListLock = doLock( keyListKey ); + try { + client.applyUpdate( new RemoveKeyUpdateAction( id, keyListKey ) ); + } + finally { + keyListLock.unlock(); + } + return message; + } + finally { + messageLock.unlock(); + } + } + + @Override + protected Collection doListKeys(String keyPattern) { + return Collections.unmodifiableSet( (Set) client.get( computeKeyListKey( keyPattern ) ).getValue() ); + } + + /** + * @param id Message or message group identifier. {@link String} type required. + * @return Identifier under which set of all currently persisted message or message group keys is saved. + */ + private String computeKeyListKey(Object id) { + final String key = (String) id; + if ( isMessageGroupKey( key ) ) { + return MESSAGE_GROUP_KEY_LIST; + } + else if ( isMessageKey( key ) ) { + return MESSAGE_KEY_LIST; + } + else { + throw new IllegalArgumentException("Unsupported identifier: " + key + "."); + } + } + + /** + * @param key Message or message group identifier. + * @return {@code true} in case of message identifier, {@code false} otherwise. + */ + private boolean isMessageKey(String key) { + return key.startsWith( AbstractKeyValueMessageStore.MESSAGE_KEY_PREFIX ); + } + + /** + * @param key Message or message group identifier. + * @return {@code true} in case of message group identifier, {@code false} otherwise. + */ + private boolean isMessageGroupKey(String key) { + return key.startsWith( AbstractKeyValueMessageStore.MESSAGE_GROUP_KEY_PREFIX ); + } + + /** + * Acquire JVM wide lock on the given object. + * @param obj Object. + * @return Lock. + */ + private Lock doLock(Object obj) { + final Lock lock = LOCK_REGISTRY.obtain( UUIDConverter.getUUID( obj ).toString() ); + lock.lock(); + return lock; + } + + /** + * Voldemort update action that adds given key to the list of currently saved identifiers. + */ + private static class AddKeyUpdateAction extends UpdateAction { + private final Object id; + private final String keyListKey; + + /** + * The only constructor. + * + * @param id Message or message group identifier. + * @param keyListKey Key under which Voldemort stores set of all currently persisted identifiers. + */ + private AddKeyUpdateAction(Object id, String keyListKey) { + this.id = id; + this.keyListKey = keyListKey; + } + + @Override + public void update(StoreClient storeClient) { + final Set keys = (Set) storeClient.get( keyListKey ).getValue(); + if ( keys.add( id ) ) { + storeClient.put( keyListKey, keys ); + } + } + } + + /** + * Voldemort update action that removes given key from the list of currently saved identifiers. + */ + private static class RemoveKeyUpdateAction extends UpdateAction { + private final Object id; + private final String keyListKey; + + /** + * The only constructor. + * + * @param id Message or message group identifier. + * @param keyListKey Key under which Voldemort stores set of all currently persisted identifiers. + */ + private RemoveKeyUpdateAction(Object id, String keyListKey) { + this.id = id; + this.keyListKey = keyListKey; + } + + @Override + public void update(StoreClient storeClient) { + final Set keys = (Set) storeClient.get( keyListKey ).getValue(); + if ( keys.remove( id ) ) { + storeClient.put( keyListKey, keys ); + } + } + } +} diff --git a/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/support/PersistMode.java b/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/support/PersistMode.java new file mode 100644 index 0000000..c4583c7 --- /dev/null +++ b/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/support/PersistMode.java @@ -0,0 +1,26 @@ +/* + * Copyright 2002-2013 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.integration.voldemort.support; + +/** + * Indicates whether records shall be added or removed from Voldemort database. + * + * @author Lukasz Antoniak + * @since 1.0 + */ +public enum PersistMode { + PUT, DELETE +} diff --git a/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/support/VoldemortHeaders.java b/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/support/VoldemortHeaders.java new file mode 100644 index 0000000..ca92796 --- /dev/null +++ b/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/support/VoldemortHeaders.java @@ -0,0 +1,36 @@ +/* + * Copyright 2002-2013 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.integration.voldemort.support; + +/** + * Voldemort adapter specific message headers. + * + * @author Lukasz Antoniak + * @since 1.0 + */ +public abstract class VoldemortHeaders { + private static final String PREFIX = "voldemort_"; + + /** + * Header representing key under which message payload should be stored. + */ + public static final String KEY = PREFIX + "_key"; + + /** + * Overrides default output adapter's persist mode for a given message. + */ + public static final String PERSIST_MODE = PREFIX + "persistMode"; +} diff --git a/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/support/package-info.java b/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/support/package-info.java new file mode 100755 index 0000000..2cf0e46 --- /dev/null +++ b/spring-integration-voldemort/src/main/java/org/springframework/integration/voldemort/support/package-info.java @@ -0,0 +1,4 @@ +/** + * Provides various support classes used across Spring Integration Voldemort components. + */ +package org.springframework.integration.voldemort.support; diff --git a/spring-integration-voldemort/src/main/resources/META-INF/spring.handlers b/spring-integration-voldemort/src/main/resources/META-INF/spring.handlers new file mode 100755 index 0000000..74e9341 --- /dev/null +++ b/spring-integration-voldemort/src/main/resources/META-INF/spring.handlers @@ -0,0 +1 @@ +http\://www.springframework.org/schema/integration/voldemort=org.springframework.integration.voldemort.config.xml.VoldemortNamespaceHandler diff --git a/spring-integration-voldemort/src/main/resources/META-INF/spring.schemas b/spring-integration-voldemort/src/main/resources/META-INF/spring.schemas new file mode 100755 index 0000000..e2e301d --- /dev/null +++ b/spring-integration-voldemort/src/main/resources/META-INF/spring.schemas @@ -0,0 +1,2 @@ +http\://www.springframework.org/schema/integration/voldemort/spring-integration-voldemort-1.0.xsd=org/springframework/integration/config/xml/spring-integration-voldemort-1.0.xsd +http\://www.springframework.org/schema/integration/voldemort/spring-integration-voldemort.xsd=org/springframework/integration/config/xml/spring-integration-voldemort-1.0.xsd diff --git a/spring-integration-voldemort/src/main/resources/META-INF/spring.tooling b/spring-integration-voldemort/src/main/resources/META-INF/spring.tooling new file mode 100755 index 0000000..c93fd86 --- /dev/null +++ b/spring-integration-voldemort/src/main/resources/META-INF/spring.tooling @@ -0,0 +1,4 @@ +# Tooling related information for the integration Voldemort namespace +http\://www.springframework.org/schema/integration/voldemort@name=integration Voldemort Namespace +http\://www.springframework.org/schema/integration/voldemort@prefix=int-voldemort +http\://www.springframework.org/schema/integration/voldemort@icon=org/springframework/integration/config/xml/spring-integration-voldemort.gif diff --git a/spring-integration-voldemort/src/main/resources/org/springframework/integration/config/xml/spring-integration-voldemort-1.0.xsd b/spring-integration-voldemort/src/main/resources/org/springframework/integration/config/xml/spring-integration-voldemort-1.0.xsd new file mode 100755 index 0000000..9b053db --- /dev/null +++ b/spring-integration-voldemort/src/main/resources/org/springframework/integration/config/xml/spring-integration-voldemort-1.0.xsd @@ -0,0 +1,187 @@ + + + + + + + + + + + + + + + Defines an Outbound Channel Adapter that writes messages to + Voldemort key-value store. + + + + + + + + Specifies the key for Voldemort store. If an expression + is needed, use 'store-key-expression' attribute. + Both options are mutually exclusive. + + + + + + + Spring Expression Language statement used to determine + desired entry key in Voldemort store. + Default expression equals 'headers.voldemort_key'. + This attribute is mutually exclusive with 'store-key' attribute. + + + + + + + Defines default persist mode for outbound-channel-adapter. + By default values are added or updated in the underlying + Voldemort database. Hereby behavior can be overridden using + VoldemortHeaders.PERSIST_MODE message header. + + + + + + + + + + Specifies the order for invocation when this endpoint is connected as a + subscriber to SubscribableChannel. + + + + + + + + + + Defines an Inbound Channel Adapter that reads records from Voldemort + key-value store. + + + + + + + + + + + String key that is being searched. + This attribute is mutually exclusive + with 'search-key-expression' attribute. + + + + + + + Spring Expression Language statement that returns + desired entry key. + This attribute is mutually exclusive with 'search-key' attribute. + + + + + + + Indicates whether returned object should be removed + from Voldemort database after polling into Spring + Integration channel. + + + + + + + + + + Flag controlling value extraction from Voldemort's + Versioned object. + + + + + + + + + + + + + Common configuration for Voldemort adapters. + + + + + + Identifies the underlying Spring bean definition. + + + + + + + + Reference to a MessageChannel that will be utilized to + "receive from" or "send to" messages depending on the + adapter type (e.g. inbound or outbound). + + + + + + + + + + + + Native Voldemort store client. + + + + + + + + + + + Flag to indicate that the component should start automatically + on startup (default true). + + + + + + + + + + + + + + + diff --git a/spring-integration-voldemort/src/main/resources/org/springframework/integration/config/xml/spring-integration-voldemort.gif b/spring-integration-voldemort/src/main/resources/org/springframework/integration/config/xml/spring-integration-voldemort.gif new file mode 100755 index 0000000..41b369f Binary files /dev/null and b/spring-integration-voldemort/src/main/resources/org/springframework/integration/config/xml/spring-integration-voldemort.gif differ diff --git a/spring-integration-voldemort/src/reference/docbook/history.xml b/spring-integration-voldemort/src/reference/docbook/history.xml new file mode 100755 index 0000000..4e9aaf7 --- /dev/null +++ b/spring-integration-voldemort/src/reference/docbook/history.xml @@ -0,0 +1,28 @@ + + + Change History + + Change History + + + + Release + Date + Changes + + + + + 1.0.0 + 1 March 2013 + + Initial release providing basic capabilities for message store, + inbound and outbound adapters. + + + + +
+
diff --git a/spring-integration-voldemort/src/reference/docbook/images/logo.png b/spring-integration-voldemort/src/reference/docbook/images/logo.png new file mode 100755 index 0000000..4f2804f Binary files /dev/null and b/spring-integration-voldemort/src/reference/docbook/images/logo.png differ diff --git a/spring-integration-voldemort/src/reference/docbook/index.xml b/spring-integration-voldemort/src/reference/docbook/index.xml new file mode 100755 index 0000000..238b413 --- /dev/null +++ b/spring-integration-voldemort/src/reference/docbook/index.xml @@ -0,0 +1,63 @@ + + + + Spring Integration Voldemort Adapter + Voldemort Adapter ${version} + Spring Integration + ${version} + + + + + + + + + + + + + + + Lukasz Antoniak + + + + © SpringSource Inc., 2013 + + + + + + + What's new? + + + If you are interested in the changes and features, that were introduced in + earlier versions, please take a look at chapter . + + + + + + + Integration Adapters + + + Spring Integration adapter for Voldemort enables users to persist data + transferred through message channel in the destination key-value store, + as well as to poll records from the Voldemort database and process them + inside Spring Integration flow. Hereby extension provides also the + implementation of a message store. + + + + + + Appendices + + + + diff --git a/spring-integration-voldemort/src/reference/docbook/resources.xml b/spring-integration-voldemort/src/reference/docbook/resources.xml new file mode 100755 index 0000000..9369e3a --- /dev/null +++ b/spring-integration-voldemort/src/reference/docbook/resources.xml @@ -0,0 +1,35 @@ + + + Additional Resources + +
+ Spring Integration Home + + The definitive source of information about Spring Integration is the + Spring Integration Home + at http://www.springsource.org. That site serves as a hub + of information and is the best place to find up-to-date announcements about the project as well as links to + articles, blogs, and new sample applications. + +
+ +
+ Project Voldemort Home + + If you are not familiar with Voldemort distributed key-value store, please visit + Project Voldemort homepage. Hereby site contains + complete design overview, list of available configuration options and documentation of client API. Links + to source code, bug tracker and wiki can be found as well. + +
+ +
+ Voldemort Adapter Integration Tests + + Integration tests of Voldemort adapter are available together with the entire source code on GitHub + (review content of src/test/java and src/test/resources packages). + +
+ +
diff --git a/spring-integration-voldemort/src/reference/docbook/voldemort.xml b/spring-integration-voldemort/src/reference/docbook/voldemort.xml new file mode 100755 index 0000000..167cf27 --- /dev/null +++ b/spring-integration-voldemort/src/reference/docbook/voldemort.xml @@ -0,0 +1,214 @@ + + + Voldemort Adapter + + The Voldemort extension for Spring Integration provides: + + + + + + Outbound Channel Adapter + + + + + + + Inbound Channel Adapter + + + + + + + Message Store + + + + + + + Each one of the components listed above requires to pass an implementation of + voldemort.client.StoreClient as constructor parameter. This way + Voldemort Adapter does not need to introduce all configuration options required to build native store client + and enables developers to choose the concrete implementation of hereby interface. + + + + + + + + + + + + +]]> + +
+ Common Configuration Attributes + + Certain configuration parameters that are shared amongst inbound and + outbound adapters have been described below. + + + + Common Adapter Attributes + + + + + + Attribute Name + Description + + + + + id + + Identifies the underlying Spring bean definition, which is an instance of either + EventDrivenConsumer or PollingConsumer + depending on whether the component's input channel is a + SubscribableChannel or PollableChannel. + Optional. + + + + channel + + Reference to a message channel that will be utilized to "receive from" or "send to" + messages depending on the adapter type (e.g. inbound or outbound). + Required. + + + + store-client + + Native Voldemort store client (implementation of + voldemort.client.StoreClient interface). + Required. + + + + auto-startup + + Lifecycle attribute signaling if this component should be started during application + context startup. Defaults to true. Optional. + + + + +
+
+ +
+ Outbound Channel Adapter + + The Voldemort Outbound channel adapter allows to persist data transferred through message channel in + the underlying key-value store. + + <int-voldemort:outbound-channel-adapter id="voldemortOutbound" + channel="voldemortOutboundChannel" + store-client="storeClient" + store-key="constant-key" + store-key-expression="payload.id" + persist-mode="DELETE" + order="1" + auto-startup="true" /> + + + + Specifies the key for Voldemort store. If an expression is needed, use + store-key-expression attribute. Both options are mutually exclusive. + Required. + + + + + Spring Expression Language statement used to determine desired entry key in Voldemort store. + Default expression equals headers.voldemort_key + (see VoldemortHeaders.KEY). This option is mutually exclusive with + store-key attribute. Required. + + + + + Defines default persist mode for outbound channel adapter. Normally values are added + or updated in the Voldemort database. Hereby behavior can be overridden on a message level using + VoldemortHeaders.PERSIST_MODE header (see PersistMode + enumeration). Supported options: PUT or DELETE. + Optional. + + + + + Specifies the order for invocation when this endpoint is connected as a subscriber to + SubscribableChannel. Optional. + + + +
+
+ Inbound Channel Adapter + + An inbound channel adapter allows to poll records from the Voldemort database and process them inside + Spring Integration flow. + + <int-voldemort:inbound-channel-adapter id="voldemortInbound" + channel="voldemortInboundChannel" + store-client="storeClient" + message-converter="messageConverter" + search-key="lukasz" + search-key-expression="@keyProducer.value" + delete-after-poll="true" + extract-value="true" + auto-startup="true" /> + + + + String key that is being searched. This option is mutually exclusive with + search-key-expression attribute. Required. + + + + + Spring Expression Language statement that returns desired entry key. Useful for keys of non string + type. This option is mutually exclusive with search-key attribute. + Required. + + + + + Indicates whether returned object should be removed from Voldemort database after polling into + Spring Integration channel. Defaults to false. Optional. + + + + + Flag controlling value extraction from Voldemort's + voldemort.versioning.Versioned object. + Defaults to true. Optional. + + + +
+
+ Message Store + + Message Store allows to persist Spring Integration messages in Voldemort database. Hereby module provides + VoldemortMessageStore which is an implementation of both the + MessageStore strategy (mainly used by QueueChannel and + ClaimCheck patterns) and the MessageGroupStore strategy + (mainly used by Aggregator and Resequencer patterns). + + + + + +]]> +
+
diff --git a/spring-integration-voldemort/src/reference/docbook/whats-new.xml b/spring-integration-voldemort/src/reference/docbook/whats-new.xml new file mode 100755 index 0000000..eab323f --- /dev/null +++ b/spring-integration-voldemort/src/reference/docbook/whats-new.xml @@ -0,0 +1,35 @@ + + + Changes in 1.0.0 + + This chapter provides an overview of new features and improvements + that have been added to the Voldemort Adapter. + + + + Inbound Channel Adapter allows to poll data from Voldemort key-value store. + Supported features: + + Periodically poll value persisted under given key. + Specify search key directly or as a Spring Expression Language statement. + Automatically remove data from Voldemort store after putting message into + Spring Integration channel. + Control adapter startup time. + + + + Outbound Channel Adapter allows to insert, update and delete data from + Voldemort key-value store. Supported features: + + Define operation type (e.g. put or delete) on adapter and message level. + Order execution when chaining with other adapters. + Control adapter startup time. + + + + Message store allows to persist Spring Integration messages in Voldemort database. + + + diff --git a/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/BaseFunctionalTestCase.java b/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/BaseFunctionalTestCase.java new file mode 100755 index 0000000..4e30167 --- /dev/null +++ b/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/BaseFunctionalTestCase.java @@ -0,0 +1,101 @@ +/* + * Copyright 2002-2013 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.integration.voldemort.test; + +import java.io.File; +import java.util.Properties; + +import org.apache.commons.io.FileUtils; +import org.junit.After; +import org.junit.Before; +import voldemort.server.VoldemortConfig; +import voldemort.server.VoldemortServer; + +/** + * Base class for functional test cases. Handles embedded Voldemort server startup and shutdown. + * + * @author Lukasz Antoniak + * @since 1.0 + */ +public abstract class BaseFunctionalTestCase { + protected VoldemortServer server = null; + + /** + * Starts Voldemort embedded server. + */ + @Before + public void setUp() throws Exception { + final VoldemortConfig config = configureServer(); + server = new VoldemortServer( config ); + server.start(); + } + + /** + * Stops Voldemort embedded server. + */ + @After + public void tearDown() throws Exception { + if ( server != null && server.isStarted() ) { + server.stop(); + } + } + + /** + * Review cluster.xml and stores.xml configuration files. + * + * @return Voldemort embedded server configuration. + */ + protected VoldemortConfig configureServer() throws Exception { + final File voldemortHome = new File( System.getProperty( "java.io.tmpdir" ), "voldemort" ); + FileUtils.deleteDirectory( voldemortHome ); + + final Properties properties = new Properties(); + properties.put( "node.id", "0" ); + properties.put( "voldemort.home", voldemortHome.getAbsolutePath() ); + addConfigOptions( properties ); + final VoldemortConfig config = new VoldemortConfig( properties ); + + final File metadata = new File( config.getMetadataDirectory() ); + FileUtils.forceMkdir( metadata ); + + FileUtils.copyFileToDirectory( getClusterConfiguration(), metadata ); + FileUtils.copyFileToDirectory( getStoreConfiguration(), metadata ); + + return config; + } + + /** + * @return Voldemort cluster configuration descriptor. + */ + protected File getClusterConfiguration() { + return new File( "src/test/resources/cluster.xml" ); + } + + /** + * @return Voldemort store configuration descriptor. + */ + protected File getStoreConfiguration() { + return new File( "src/test/resources/stores.xml" ); + } + + /** + * Subclasses may want to setup specific server configuration parameters. + * + * @param properties Voldemort server configuration properties. + */ + protected void addConfigOptions(Properties properties) { + } +} diff --git a/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/domain/Car.java b/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/domain/Car.java new file mode 100644 index 0000000..0b81119 --- /dev/null +++ b/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/domain/Car.java @@ -0,0 +1,123 @@ +/* + * Copyright 2002-2013 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.integration.voldemort.test.domain; + +import java.io.Serializable; + +/** + * Sample object with composite key persisted in Voldemort database. + * + * @author Lukasz Antoniak + * @since 1.0 + */ +public class Car implements Serializable { + private static final long serialVersionUID = -891019943116582242L; + + private CarId id; + private String model; + + public Car(CarId id, String model) { + this.id = id; + this.model = model; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) return true; + if ( ! ( o instanceof Car ) ) return false; + + Car car = (Car) o; + + if ( id != null ? !id.equals( car.id ) : car.id != null ) return false; + if ( model != null ? !model.equals( car.model ) : car.model != null ) return false; + + return true; + } + + @Override + public int hashCode() { + int result = id != null ? id.hashCode() : 0; + result = 31 * result + ( model != null ? model.hashCode() : 0 ); + return result; + } + + @Override + public String toString() { + return "Car(id = " + id + ", model = " + model + ")"; + } + + public CarId getId() { + return id; + } + + public void setId(CarId id) { + this.id = id; + } + + public String getModel() { + return model; + } + + public void setModel(String model) { + this.model = model; + } + + /** + * Car identifier wrapper. + * + * @author Lukasz Antoniak + * @since 1.0 + */ + public static class CarId implements Serializable { + private static final long serialVersionUID = -5586075844887213095L; + + private Integer id; + + public CarId(Integer id) { + this.id = id; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) return true; + if ( ! ( o instanceof CarId ) ) return false; + + CarId carId = (CarId) o; + + if ( id != null ? !id.equals( carId.id ) : carId.id != null ) return false; + + return true; + } + + @Override + public int hashCode() { + return id != null ? id.hashCode() : 0; + } + + @Override + public String toString() { + return "CarId(id = " + id + ")"; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + } +} diff --git a/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/domain/Person.java b/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/domain/Person.java new file mode 100755 index 0000000..071279d --- /dev/null +++ b/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/domain/Person.java @@ -0,0 +1,89 @@ +/* + * Copyright 2002-2013 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.integration.voldemort.test.domain; + +import java.io.Serializable; + +/** + * Sample object persisted in Voldemort database. + * + * @author Lukasz Antoniak + * @since 1.0 + */ +public class Person implements Serializable { + private static final long serialVersionUID = -9092199331950213292L; + + private String id; + private String firstName; + private String lastName; + + public Person(String id, String firstName, String lastName) { + this.id = id; + this.firstName = firstName; + this.lastName = lastName; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) return true; + if ( !( o instanceof Person ) ) return false; + + Person person = (Person) o; + + if ( id != null ? !id.equals( person.id ) : person.id != null ) return false; + if ( firstName != null ? !firstName.equals( person.firstName ) : person.firstName != null ) return false; + if ( lastName != null ? !lastName.equals( person.lastName ) : person.lastName != null ) return false; + + return true; + } + + @Override + public int hashCode() { + int result = id != null ? id.hashCode() : 0; + result = 31 * result + ( firstName != null ? firstName.hashCode() : 0 ); + result = 31 * result + ( lastName != null ? lastName.hashCode() : 0 ); + return result; + } + + @Override + public String toString() { + return "Person(id = " + id + ", firstName = " + firstName + ", lastName = " + lastName + ")"; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } +} diff --git a/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/inbound/ObjectKeyTest.java b/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/inbound/ObjectKeyTest.java new file mode 100644 index 0000000..ad6471e --- /dev/null +++ b/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/inbound/ObjectKeyTest.java @@ -0,0 +1,66 @@ +/* + * Copyright 2002-2013 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.integration.voldemort.test.inbound; + +import junit.framework.Assert; +import org.junit.Test; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.integration.Message; +import org.springframework.integration.core.PollableChannel; +import org.springframework.integration.voldemort.test.BaseFunctionalTestCase; +import org.springframework.integration.voldemort.test.domain.Car; +import voldemort.client.StoreClient; + +/** + * Test key of object type. + * + * @author Lukasz Antoniak + * @since 1.0 + */ +@SuppressWarnings("unchecked") +public class ObjectKeyTest extends BaseFunctionalTestCase { + @Test + public void testReceiveMessageByObjectKey() { + final ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "ObjectKeyTest-context.xml", getClass() ); + final StoreClient storeClient = context.getBean( "objectStoreClient", StoreClient.class ); + final PollableChannel inboundChannel = context.getBean( "voldemortInboundChannel", PollableChannel.class ); + + // given + final Car.CarId carId = new Car.CarId( 1 ); + final Car car = new Car( carId, "Ford Mustang" ); + storeClient.put( carId, car ); + + // when + final Message received = (Message) inboundChannel.receive(); + + // then + Assert.assertEquals( car, received.getPayload() ); + + context.close(); + } + + /** + * Produces key of object type. + * + * @author Lukasz Antoniak + * @since 1.0 + */ + public static class ObjectKeyProducer { + public Object getValue() { + return new Car.CarId( 1 ); + } + } +} diff --git a/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/inbound/VoldemortInboundAdapterTest.java b/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/inbound/VoldemortInboundAdapterTest.java new file mode 100644 index 0000000..e52617e --- /dev/null +++ b/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/inbound/VoldemortInboundAdapterTest.java @@ -0,0 +1,101 @@ +/* + * Copyright 2002-2013 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.integration.voldemort.test.inbound; + +import junit.framework.Assert; +import org.junit.Test; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.integration.Message; +import org.springframework.integration.core.PollableChannel; +import org.springframework.integration.voldemort.test.BaseFunctionalTestCase; +import org.springframework.integration.voldemort.test.domain.Person; +import voldemort.client.StoreClient; +import voldemort.versioning.Versioned; + +/** + * Voldemort basic inbound adapter tests. + * + * @author Lukasz Antoniak + * @since 1.0 + */ +@SuppressWarnings("unchecked") +public class VoldemortInboundAdapterTest extends BaseFunctionalTestCase { + /** + * Tests inbound adapter configured with "search-key" attribute. + */ + @Test + public void testReceiveMessageKey() { + final ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "VoldemortInboundAdapterTest-context.xml", getClass() ); + final StoreClient storeClient = context.getBean( "storeClient", StoreClient.class ); + final PollableChannel inboundChannel = context.getBean( "voldemortInboundChannel", PollableChannel.class ); + + // given + final Person lukasz = new Person( "lukasz", "Lukasz", "Antoniak" ); + storeClient.put( lukasz.getId(), lukasz ); + + // when + final Message received = (Message) inboundChannel.receive(); + + // then + Assert.assertEquals( lukasz, received.getPayload() ); + + context.close(); + } + + /** + * Tests inbound adapter configured with "search-key-expression" attribute. + */ + @Test + public void testReceiveMessageExpr() { + final ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "VoldemortInboundAdapterTest-context.xml", getClass() ); + final StoreClient storeClient = context.getBean( "storeClient", StoreClient.class ); + final PollableChannel inboundChannel = context.getBean( "voldemortInboundChannel", PollableChannel.class ); + + // given + final Person kinga = new Person( "kinga", "Kinga", "Mroz" ); + storeClient.put( kinga.getId(), kinga ); + + // when + final Message> received = (Message>) inboundChannel.receive(); + + // then + final Versioned found = storeClient.get( kinga.getId() ); + Assert.assertEquals( found, received.getPayload() ); + + context.close(); + } + + @Test + public void testDeleteAfterPoll() { + final ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "VoldemortInboundAdapterTest-context.xml", getClass() ); + final StoreClient storeClient = context.getBean( "storeClient", StoreClient.class ); + final PollableChannel inboundChannel = context.getBean( "voldemortInboundChannel", PollableChannel.class ); + + // given + final Person robert = new Person( "robert", "Robert", "Antoniak" ); + storeClient.put( robert.getId(), robert ); + + // when + final Message received = (Message) inboundChannel.receive(); + + // then + Assert.assertEquals( robert, received.getPayload() ); + final Versioned found = storeClient.get( robert.getId() ); + Assert.assertNull( found ); + + context.close(); + } +} diff --git a/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/outbound/MessageUpdatingServiceActivator.java b/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/outbound/MessageUpdatingServiceActivator.java new file mode 100644 index 0000000..520940c --- /dev/null +++ b/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/outbound/MessageUpdatingServiceActivator.java @@ -0,0 +1,38 @@ +/* + * Copyright 2002-2013 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.integration.voldemort.test.outbound; + +import org.springframework.integration.Message; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.integration.voldemort.test.domain.Person; + +/** + * Sample service activator used to verify order of output adapters execution. + * + * @author Lukasz Antoniak + * @since 1.0 + */ +public class MessageUpdatingServiceActivator { + @ServiceActivator + public Message updateMessage(Message message) { + updatePerson( message.getPayload() ); + return message; + } + + protected void updatePerson(Person person) { + person.setFirstName( "Robert" ); + } +} diff --git a/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/outbound/ObjectKeyTest.java b/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/outbound/ObjectKeyTest.java new file mode 100644 index 0000000..bdec184 --- /dev/null +++ b/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/outbound/ObjectKeyTest.java @@ -0,0 +1,58 @@ +/* + * Copyright 2002-2013 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.integration.voldemort.test.outbound; + +import junit.framework.Assert; +import org.junit.Test; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.integration.Message; +import org.springframework.integration.MessageChannel; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.integration.voldemort.support.VoldemortHeaders; +import org.springframework.integration.voldemort.test.BaseFunctionalTestCase; +import org.springframework.integration.voldemort.test.domain.Car; +import voldemort.client.StoreClient; +import voldemort.versioning.Versioned; + +/** + * Test key of object type. + * + * @author Lukasz Antoniak + * @since 1.0 + */ +@SuppressWarnings("unchecked") +public class ObjectKeyTest extends BaseFunctionalTestCase { + @Test + public void testCompositeKey() { + final ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "ObjectKeyTest-context.xml", getClass() ); + final StoreClient storeClient = context.getBean( "objectStoreClient", StoreClient.class ); + final MessageChannel voldemortOutboundPutChannel = context.getBean( "voldemortOutboundPutChannel", MessageChannel.class ); + + // given + final Car.CarId carId = new Car.CarId( 1 ); + final Car car = new Car( carId, "Ford Mustang" ); + + // when + final Message message = MessageBuilder.withPayload( car ).setHeader( VoldemortHeaders.KEY, carId ).build(); + voldemortOutboundPutChannel.send( message ); + + // then + final Versioned found = storeClient.get( carId ); + Assert.assertEquals( car, found.getValue() ); + + context.close(); + } +} diff --git a/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/outbound/VoldemortOutboundAdapterTest.java b/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/outbound/VoldemortOutboundAdapterTest.java new file mode 100755 index 0000000..588cba8 --- /dev/null +++ b/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/outbound/VoldemortOutboundAdapterTest.java @@ -0,0 +1,177 @@ +/* + * Copyright 2002-2013 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.integration.voldemort.test.outbound; + +import junit.framework.Assert; +import org.junit.Test; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.integration.Message; +import org.springframework.integration.MessageChannel; +import org.springframework.integration.MessageDeliveryException; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.integration.voldemort.support.PersistMode; +import org.springframework.integration.voldemort.support.VoldemortHeaders; +import org.springframework.integration.voldemort.test.BaseFunctionalTestCase; +import org.springframework.integration.voldemort.test.domain.Person; +import voldemort.client.StoreClient; +import voldemort.versioning.Versioned; + +/** + * Voldemort basic outbound adapter tests. + * + * @author Lukasz Antoniak + * @since 1.0 + */ +@SuppressWarnings("unchecked") +public class VoldemortOutboundAdapterTest extends BaseFunctionalTestCase { + @Test + public void testPutObject() { + final ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "VoldemortOutboundAdapterTest-context.xml", getClass() ); + final StoreClient storeClient = context.getBean( "storeClient", StoreClient.class ); + final MessageChannel voldemortOutboundPutChannel = context.getBean( "voldemortOutboundPutChannel", MessageChannel.class ); + + // given + final Person lukasz = new Person( "1", "Lukasz", "Antoniak" ); + + // when + final Message message = MessageBuilder.withPayload( lukasz ).setHeader( VoldemortHeaders.KEY, lukasz.getId() ).build(); + voldemortOutboundPutChannel.send( message ); + + // then + final Versioned found = storeClient.get( lukasz.getId() ); + Assert.assertEquals( lukasz, found.getValue() ); + + context.close(); + } + + @Test + public void testPutObjectsConstantKey() { + final ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "VoldemortOutboundAdapterTest-context.xml", getClass() ); + final StoreClient storeClient = context.getBean( "storeClient", StoreClient.class ); + final MessageChannel voldemortOutboundChannel = context.getBean( "voldemortOutboundPutConstantKeyChannel", MessageChannel.class ); + + // given + final Person lukasz = new Person( "1", "Lukasz", "Antoniak" ); + + // when + final Message firstMessage = MessageBuilder.withPayload( lukasz ).build(); + voldemortOutboundChannel.send( firstMessage ); + + // then + Assert.assertEquals( lukasz, storeClient.get( "constant-key" ).getValue() ); + + // given + final Person tomasz = new Person( "2", "Tomasz", "Antoniak" ); + + // when + final Message secondMessage = MessageBuilder.withPayload( tomasz ).build(); + voldemortOutboundChannel.send( secondMessage ); + + // then + Assert.assertEquals( tomasz, storeClient.get( "constant-key" ).getValue() ); + + context.close(); + } + + @Test + public void testDeleteObject() { + final ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "VoldemortOutboundAdapterTest-context.xml", getClass() ); + final StoreClient storeClient = context.getBean( "storeClient", StoreClient.class ); + final MessageChannel voldemortOutboundDeleteChannel = context.getBean( "voldemortOutboundDeleteChannel", MessageChannel.class ); + + // given + final Person lukasz = new Person( "1", "Lukasz", "Antoniak" ); + storeClient.put( lukasz.getId(), lukasz ); + + // when + final Message message = MessageBuilder.withPayload( lukasz ).build(); + voldemortOutboundDeleteChannel.send( message ); + + // then + final Versioned found = storeClient.get( lukasz.getId() ); + Assert.assertNull( found ); + + context.close(); + } + + @Test + public void testOverridePersistMode() { + final ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "VoldemortOutboundAdapterTest-context.xml", getClass() ); + final StoreClient storeClient = context.getBean( "storeClient", StoreClient.class ); + final MessageChannel voldemortOutboundDeleteChannel = context.getBean( "voldemortOutboundDeleteChannel", MessageChannel.class ); + + // given + final Person lukasz = new Person( "1", "Lukasz", "Antoniak" ); + + // when + // Overriding output adapter's persist mode. + final Message message = MessageBuilder.withPayload( lukasz ) + .setHeader( VoldemortHeaders.PERSIST_MODE, PersistMode.PUT ).build(); + voldemortOutboundDeleteChannel.send( message ); + + // then + final Versioned found = storeClient.get( lukasz.getId() ); + Assert.assertEquals( lukasz, found.getValue() ); + + context.close(); + } + + @Test + public void testStoppedAdapter() { + final ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "VoldemortOutboundAdapterTest-context.xml", getClass() ); + final MessageChannel voldemortStoppedChannel = context.getBean( "voldemortStoppedChannel", MessageChannel.class ); + + // given + final Person lukasz = new Person( "1", "Lukasz", "Antoniak" ); + + // when + final Message message = MessageBuilder.withPayload( lukasz ).build(); + try { + voldemortStoppedChannel.send( message ); + } + catch ( MessageDeliveryException e ) { + return; + } + finally { + context.close(); + } + + Assert.fail(); + } + + @Test + public void testOrder() { + final ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "VoldemortOutboundAdapterTest-context.xml", getClass() ); + final StoreClient storeClient = context.getBean( "storeClient", StoreClient.class ); + final MessageUpdatingServiceActivator messageUpdater = context.getBean( "messageUpdater", MessageUpdatingServiceActivator.class ); + final MessageChannel voldemortOrderChannel = context.getBean( "voldemortOrderChannel", MessageChannel.class ); + + // given + final Person lukasz = new Person( "lukasz", "Lukasz", "Antoniak" ); + final Person copy = new Person( "lukasz", "Lukasz", "Antoniak" ); + + // when + final Message message = MessageBuilder.withPayload( lukasz ).build(); + voldemortOrderChannel.send( message ); + + // then + messageUpdater.updatePerson( copy ); + final Versioned found = storeClient.get( lukasz.getId() ); + Assert.assertEquals( copy, found.getValue() ); + + context.close(); + } +} diff --git a/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/store/BaseStoreFunctionalTestCase.java b/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/store/BaseStoreFunctionalTestCase.java new file mode 100644 index 0000000..8fc8641 --- /dev/null +++ b/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/store/BaseStoreFunctionalTestCase.java @@ -0,0 +1,44 @@ +package org.springframework.integration.voldemort.test.store; + +import java.io.File; + +import org.junit.After; +import org.junit.Before; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.integration.voldemort.store.VoldemortMessageStore; +import org.springframework.integration.voldemort.test.BaseFunctionalTestCase; + +/** + * Base class for message store test cases. + * + * @author Lukasz Antoniak + * @since 1.0 + */ +public abstract class BaseStoreFunctionalTestCase extends BaseFunctionalTestCase { + protected ClassPathXmlApplicationContext context = null; + protected VoldemortMessageStore store = null; + + @Override + protected File getStoreConfiguration() { + return new File( "src/test/resources/org/springframework/integration/voldemort/test/store/stores.xml" ); + } + + @Before + public void contextSetup() { + context = new ClassPathXmlApplicationContext( "store-test-context.xml", getClass() ); + store = context.getBean( "voldemortMessageStore", VoldemortMessageStore.class ); + } + + @After + public void contextDestroy() { + store = null; + if ( context != null ) { + context.close(); + context = null; + } + } + + protected VoldemortMessageStore createNewStoreClient() { + return context.getBean( "voldemortMessageStorePrototype", VoldemortMessageStore.class ); + } +} diff --git a/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/store/VoldemortMessageGroupStoreTest.java b/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/store/VoldemortMessageGroupStoreTest.java new file mode 100644 index 0000000..40e1f0f --- /dev/null +++ b/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/store/VoldemortMessageGroupStoreTest.java @@ -0,0 +1,316 @@ +/* + * Copyright 2002-2013 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.integration.voldemort.test.store; + +import java.util.Iterator; +import java.util.Properties; +import java.util.Random; +import java.util.UUID; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import org.junit.Assert; +import org.junit.Test; +import org.springframework.integration.Message; +import org.springframework.integration.channel.DirectChannel; +import org.springframework.integration.history.MessageHistory; +import org.springframework.integration.message.GenericMessage; +import org.springframework.integration.store.MessageGroup; +import org.springframework.integration.store.SimpleMessageGroup; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.integration.voldemort.store.VoldemortMessageStore; + +/** + * Voldemort message store tests based on Redis module. + * + * @author Lukasz Antoniak + * @since 1.0 + */ +@SuppressWarnings("unchecked") +public class VoldemortMessageGroupStoreTest extends BaseStoreFunctionalTestCase { + @Test + public void testNonExistingEmptyMessageGroup() { + final MessageGroup messageGroup = store.getMessageGroup( 1 ); + + Assert.assertNotNull( messageGroup ); + Assert.assertTrue( messageGroup instanceof SimpleMessageGroup ); + Assert.assertEquals( 0, messageGroup.size() ); + } + + @Test + public void testUpdatedDateChangesWithEachAddedMessage() throws InterruptedException { + MessageGroup messageGroup = store.getMessageGroup( 1 ); + Message message = new GenericMessage( "Hello" ); + messageGroup = store.addMessageToGroup( 1, message ); + Assert.assertEquals( 1, messageGroup.size() ); + + long createdTimestamp = messageGroup.getTimestamp(); + long updatedTimestamp = messageGroup.getLastModified(); + Assert.assertEquals( createdTimestamp, updatedTimestamp ); + Thread.sleep( 1000 ); + + message = new GenericMessage( "Hello" ); + messageGroup = store.addMessageToGroup( 1, message ); + createdTimestamp = messageGroup.getTimestamp(); + updatedTimestamp = messageGroup.getLastModified(); + Assert.assertTrue( updatedTimestamp > createdTimestamp ); + + // use another message store instance + VoldemortMessageStore newStore = createNewStoreClient(); + + messageGroup = newStore.getMessageGroup( 1 ); + Assert.assertEquals( 2, messageGroup.size() ); + } + + @Test + public void testMessageGroupAddOperation() { + MessageGroup messageGroup = store.getMessageGroup( 1 ); + Message message = new GenericMessage( "Hello" ); + messageGroup = store.addMessageToGroup( 1, message ); + Assert.assertEquals( 1, messageGroup.size() ); + + // use another message store instance + VoldemortMessageStore newStore = createNewStoreClient(); + + messageGroup = newStore.getMessageGroup( 1 ); + Assert.assertEquals( 1, messageGroup.size() ); + } + + @Test + public void testMessageGroupWithAddedMessageUUIDGroupIdAndUUIDHeader() { + final Object id = UUID.randomUUID(); + MessageGroup messageGroup = store.getMessageGroup( id ); + final UUID uuidA = UUID.randomUUID(); + Message messageA = MessageBuilder.withPayload( "A" ).setHeader( "foo", uuidA ).build(); + final UUID uuidB = UUID.randomUUID(); + Message messageB = MessageBuilder.withPayload( "B" ).setHeader( "foo", uuidB ).build(); + store.addMessageToGroup( id, messageA ); + messageGroup = store.addMessageToGroup( id, messageB ); + Assert.assertEquals( 2, messageGroup.size() ); + Message retrievedMessage = store.getMessage( messageA.getHeaders().getId() ); + Assert.assertNotNull( retrievedMessage ); + Assert.assertEquals( retrievedMessage.getHeaders().getId(), messageA.getHeaders().getId() ); + Object fooHeader = retrievedMessage.getHeaders().get( "foo" ); + Assert.assertTrue( fooHeader instanceof UUID ); + Assert.assertEquals( uuidA, fooHeader ); + } + + @Test + public void testCountMessagesInGroup() { + Message messageA = new GenericMessage( "A" ); + Message messageB = new GenericMessage( "B" ); + store.addMessageToGroup( 1, messageA ); + store.addMessageToGroup( 1, messageB ); + Assert.assertEquals( 2, store.messageGroupSize( 1 ) ); + } + + @Test + public void testRemoveMessageGroup() { + MessageGroup messageGroup = store.getMessageGroup( 1 ); + Message message = new GenericMessage( "Hello" ); + messageGroup = store.addMessageToGroup( messageGroup.getGroupId(), message ); + Assert.assertEquals( 1, messageGroup.size() ); + + store.removeMessageGroup( 1 ); + + MessageGroup messageGroupA = store.getMessageGroup( 1 ); + Assert.assertNotSame( messageGroup, messageGroupA ); + Assert.assertEquals( 0, messageGroupA.getMessages().size() ); + Assert.assertEquals( 0, messageGroupA.size() ); + + // use another message store instance + VoldemortMessageStore newStore = createNewStoreClient(); + + messageGroup = newStore.getMessageGroup( 1 ); + + Assert.assertEquals( 0, messageGroup.getMessages().size() ); + Assert.assertEquals( 0, messageGroup.size() ); + } + + @Test + public void testCompleteMessageGroup() { + MessageGroup messageGroup = store.getMessageGroup( 1 ); + Message message = new GenericMessage( "Hello" ); + messageGroup = store.addMessageToGroup( messageGroup.getGroupId(), message ); + store.completeGroup( messageGroup.getGroupId() ); + messageGroup = store.getMessageGroup( 1 ); + Assert.assertTrue( messageGroup.isComplete() ); + } + + @Test + public void testLastReleasedSequenceNumber() { + MessageGroup messageGroup = store.getMessageGroup( 1 ); + Message message = new GenericMessage( "Hello" ); + messageGroup = store.addMessageToGroup( messageGroup.getGroupId(), message ); + store.setLastReleasedSequenceNumberForGroup( messageGroup.getGroupId(), 5 ); + messageGroup = store.getMessageGroup( 1 ); + Assert.assertEquals( 5, messageGroup.getLastReleasedMessageSequenceNumber() ); + } + + @Test + public void testRemoveMessageFromTheGroup() { + MessageGroup messageGroup = store.getMessageGroup( 1 ); + Message message = new GenericMessage( "2" ); + store.addMessageToGroup( messageGroup.getGroupId(), new GenericMessage( "1" ) ); + store.addMessageToGroup( messageGroup.getGroupId(), message ); + messageGroup = store.addMessageToGroup( messageGroup.getGroupId(), new GenericMessage( "3" ) ); + Assert.assertEquals( 3, messageGroup.size() ); + + messageGroup = store.removeMessageFromGroup( 1, message ); + Assert.assertEquals( 2, messageGroup.size() ); + + // use another message store instance + VoldemortMessageStore newStore = createNewStoreClient(); + + messageGroup = newStore.getMessageGroup( 1 ); + Assert.assertEquals( 2, messageGroup.size() ); + } + + @Test + public void testWithMessageHistory() { + MessageGroup messageGroup = store.getMessageGroup( 1 ); + Message message = new GenericMessage( "Hello" ); + DirectChannel fooChannel = new DirectChannel(); + fooChannel.setBeanName( "fooChannel" ); + DirectChannel barChannel = new DirectChannel(); + barChannel.setBeanName( "barChannel" ); + + message = MessageHistory.write( message, fooChannel ); + message = MessageHistory.write( message, barChannel ); + store.addMessageToGroup( 1, message ); + message = store.getMessageGroup( 1 ).getMessages().iterator().next(); + MessageHistory messageHistory = MessageHistory.read( message ); + + Assert.assertNotNull( messageHistory ); + Assert.assertEquals( 2, messageHistory.size() ); + + Properties fooChannelHistory = messageHistory.get( 0 ); + + Assert.assertEquals( "fooChannel", fooChannelHistory.get( "name" ) ); + Assert.assertEquals( "channel", fooChannelHistory.get( "type" ) ); + } + + @Test + public void testRemoveNonExistingMessageFromGroup() { + MessageGroup messageGroup = store.getMessageGroup( 1 ); + store.addMessageToGroup( messageGroup.getGroupId(), new GenericMessage( "1" ) ); + store.removeMessageFromGroup( messageGroup.getGroupId(), new GenericMessage( "2" ) ); + } + + @Test + public void testRemoveNonExistingMessageFromNonExistingGroup() { + store.removeMessageFromGroup( 1, new GenericMessage( "2" ) ); + } + + @Test + public void testMultipleInstancesOfGroupStore() { + VoldemortMessageStore store1 = createNewStoreClient(); + VoldemortMessageStore store2 = createNewStoreClient(); + + Message message = new GenericMessage( "1" ); + store1.addMessageToGroup( 1, message ); + MessageGroup messageGroup = store2.addMessageToGroup( 1, new GenericMessage( "2" ) ); + Assert.assertEquals( 2, messageGroup.getMessages().size() ); + + VoldemortMessageStore store3 = createNewStoreClient(); + messageGroup = store3.removeMessageFromGroup( 1, message ); + Assert.assertEquals( 1, messageGroup.getMessages().size() ); + } + + @Test + public void testIteratorOfMessageGroups() { + VoldemortMessageStore store1 = createNewStoreClient(); + VoldemortMessageStore store2 = createNewStoreClient(); + + store1.addMessageToGroup( 1, new GenericMessage( "1" ) ); + store2.addMessageToGroup( 2, new GenericMessage( "2" ) ); + store1.addMessageToGroup( 3, new GenericMessage( "3" ) ); + store2.addMessageToGroup( 3, new GenericMessage( "3A" ) ); + + Iterator messageGroups = store1.iterator(); + int counter = 0; + while ( messageGroups.hasNext() ) { + final MessageGroup group = messageGroups.next(); + final String groupId = (String) group.getGroupId(); + if ( "1".equals( groupId ) ) { + Assert.assertEquals( 1, group.getMessages().size() ); + } + else if ( "2".equals( groupId ) ) { + Assert.assertEquals( 1, group.getMessages().size() ); + } + else if ( "3".equals( groupId ) ) { + Assert.assertEquals( 2, group.getMessages().size() ); + } + ++counter; + } + Assert.assertEquals( 3, counter ); + + store2.removeMessageGroup( 3 ); + + messageGroups = store1.iterator(); + counter = 0; + while ( messageGroups.hasNext() ) { + messageGroups.next(); + ++counter; + } + Assert.assertEquals( 2, counter ); + } + + @Test + public void testConcurrentModifications() throws InterruptedException { + final VoldemortMessageStore store1 = createNewStoreClient(); + final VoldemortMessageStore store2 = createNewStoreClient(); + + final ExecutorService executor = Executors.newCachedThreadPool(); + final Counter errorCounter = new Counter(); + final Random randomGenerator = new Random(); + for ( int i = 0; i < 10; ++i ) { + executor.execute( new Runnable() { + public void run() { + try { + final Message message = new GenericMessage( UUID.randomUUID() ); + MessageGroup group = store1.addMessageToGroup( 1, message ); + + Thread.sleep( randomGenerator.nextInt( 100 ) ); + + group = store2.removeMessageFromGroup( 1, message ); + } + catch ( Exception e ) { + errorCounter.increment(); + } + } + }); + } + executor.shutdown(); + executor.awaitTermination( 10, TimeUnit.SECONDS ); + + Assert.assertEquals( 0, errorCounter.getValue() ); + Assert.assertEquals( 0, store1.getMessageCount() ); + } + + private static class Counter { + private int value = 0; + + public synchronized int increment() { + return ++value; + } + + public int getValue() { + return value; + } + } +} diff --git a/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/store/VoldemortMessageStoreAggregationTest.java b/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/store/VoldemortMessageStoreAggregationTest.java new file mode 100644 index 0000000..c38016e --- /dev/null +++ b/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/store/VoldemortMessageStoreAggregationTest.java @@ -0,0 +1,70 @@ +/* + * Copyright 2002-2013 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.integration.voldemort.test.store; + +import java.io.File; + +import org.junit.Assert; +import org.junit.Test; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.integration.Message; +import org.springframework.integration.MessageChannel; +import org.springframework.integration.channel.QueueChannel; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.integration.voldemort.test.BaseFunctionalTestCase; + +/** + * Voldemort message store tests based on Redis module. + * + * @author Lukasz Antoniak + * @since 1.0 + */ +@SuppressWarnings("unchecked") +public class VoldemortMessageStoreAggregationTest extends BaseFunctionalTestCase { + @Override + protected File getStoreConfiguration() { + return new File( "src/test/resources/org/springframework/integration/voldemort/test/store/stores.xml" ); + } + + @Test + public void testAggregatorWithShutdown() { + ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "VoldemortMessageStoreAggregationTest-context.xml", getClass() ); + MessageChannel input = context.getBean( "inputChannel", MessageChannel.class ); + QueueChannel output = context.getBean( "outputChannel", QueueChannel.class ); + + Message message1 = MessageBuilder.withPayload( "1" ) + .setSequenceNumber( 1 ).setSequenceSize( 3 ).setCorrelationId( 1 ).build(); + Message message2 = MessageBuilder.withPayload( "2" ) + .setSequenceNumber( 2 ).setSequenceSize( 3 ).setCorrelationId( 1 ).build(); + input.send( message1 ); + Assert.assertNull( output.receive( 1000 ) ); + input.send( message2 ); + Assert.assertNull( output.receive( 1000 ) ); + + context.close(); + + context = new ClassPathXmlApplicationContext( "VoldemortMessageStoreAggregationTest-context.xml", getClass() ); + input = context.getBean( "inputChannel", MessageChannel.class ); + output = context.getBean( "outputChannel", QueueChannel.class ); + + Message message3 = MessageBuilder.withPayload( "3" ) + .setSequenceNumber( 3 ).setSequenceSize( 3 ).setCorrelationId( 1 ).build(); + input.send( message3 ); + Assert.assertNotNull( output.receive( 1000 ) ); + + context.close(); + } +} diff --git a/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/store/VoldemortMessageStoreTest.java b/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/store/VoldemortMessageStoreTest.java new file mode 100644 index 0000000..415e604 --- /dev/null +++ b/spring-integration-voldemort/src/test/java/org/springframework/integration/voldemort/test/store/VoldemortMessageStoreTest.java @@ -0,0 +1,187 @@ +/* + * Copyright 2002-2013 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.integration.voldemort.test.store; + +import java.io.Serializable; +import java.util.Properties; +import java.util.UUID; + +import org.junit.Assert; +import org.junit.Test; +import org.springframework.integration.Message; +import org.springframework.integration.channel.DirectChannel; +import org.springframework.integration.history.MessageHistory; +import org.springframework.integration.message.GenericMessage; + +/** + * Voldemort message store tests based on Redis module. + * + * @author Lukasz Antoniak + * @since 1.0 + */ +@SuppressWarnings("unchecked") +public class VoldemortMessageStoreTest extends BaseStoreFunctionalTestCase { + @Test + public void testGetNonExistingMessage() { + final Message message = store.getMessage( UUID.randomUUID() ); + + Assert.assertNull( message ); + } + + @Test + public void testGetMessageCountWhenEmpty() { + Assert.assertEquals( 0, store.getMessageCount() ); + } + + @Test + public void testAddStringMessage() { + final Message stringMessage = new GenericMessage( "Hello Voldemort" ); + + final Message storedMessage = store.addMessage( stringMessage ); + + Assert.assertNotSame( stringMessage, storedMessage ); + Assert.assertEquals( stringMessage.getPayload(), storedMessage.getPayload() ); + } + + @Test + public void testAddSerializableObjectMessage() { + final Address address = new Address( "1600 Pennsylvania Av, Washington, DC" ); + final Person person = new Person( "Barack Obama", address ); + Message objectMessage = new GenericMessage( person ); + + Message storedMessage = store.addMessage( objectMessage ); + + Assert.assertNotSame( objectMessage, storedMessage ); + Assert.assertEquals( person.getName(), storedMessage.getPayload().getName() ); + Assert.assertEquals( person.getAddress().getAddress(), storedMessage.getPayload().getAddress().getAddress() ); + } + + @Test(expected = IllegalArgumentException.class) + public void testAddNonSerializableObjectMessage() { + Message objectMessage = new GenericMessage( new Foo() ); + store.addMessage( objectMessage ); + } + + @Test + public void testAddAndGetStringMessage() { + final Message stringMessage = new GenericMessage( "Hello Voldemort" ); + + store.addMessage( stringMessage ); + final Message retrievedMessage = (Message) store.getMessage( stringMessage.getHeaders().getId() ); + + Assert.assertNotNull( retrievedMessage ); + Assert.assertEquals( stringMessage.getPayload(), retrievedMessage.getPayload() ); + } + + @Test + public void testAddAndRemoveStringMessage() { + final Message stringMessage = new GenericMessage( "Hello Voldemort" ); + + store.addMessage(stringMessage); + Message retrievedMessage = (Message) store.removeMessage( stringMessage.getHeaders().getId() ); + + Assert.assertNotNull( retrievedMessage ); + Assert.assertEquals( stringMessage.getPayload(), retrievedMessage.getPayload() ); + Assert.assertNull( store.getMessage( stringMessage.getHeaders().getId() ) ); + } + + @Test + public void testMessageCount() { + final Message stringMessage1 = new GenericMessage( "Hello Voldemort" ); + final Message stringMessage2 = new GenericMessage( "Hello World" ); + + store.addMessage( stringMessage1 ); + Assert.assertEquals( 1, store.getMessageCount() ); + + store.addMessage( stringMessage2 ); + Assert.assertEquals( 2, store.getMessageCount() ); + + store.removeMessage( stringMessage1.getHeaders().getId() ); + Assert.assertEquals( 1, store.getMessageCount() ); + } + + @Test + public void testWithMessageHistory() { + Message message = new GenericMessage( "Hello" ); + final DirectChannel fooChannel = new DirectChannel(); + fooChannel.setBeanName( "fooChannel" ); + final DirectChannel barChannel = new DirectChannel(); + barChannel.setBeanName( "barChannel" ); + + message = MessageHistory.write( message, fooChannel ); + message = MessageHistory.write( message, barChannel ); + store.addMessage( message ); + message = store.getMessage( message.getHeaders().getId() ); + MessageHistory messageHistory = MessageHistory.read( message ); + + Assert.assertNotNull( messageHistory ); + Assert.assertEquals( 2, messageHistory.size() ); + + Properties fooChannelHistory = messageHistory.get( 0 ); + + Assert.assertEquals( "fooChannel", fooChannelHistory.get( "name" ) ); + Assert.assertEquals( "channel", fooChannelHistory.get( "type" ) ); + } + + public static class Person implements Serializable { + private static final long serialVersionUID = 6109955909562732898L; + + private String name; + private Address address; + + public Person(String name, Address address) { + this.name = name; + this.address = address; + } + + public Address getAddress() { + return address; + } + + public void setAddress(Address address) { + this.address = address; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + public static class Address implements Serializable { + private static final long serialVersionUID = 2382619388682259472L; + + private String address; + + public Address(String address) { + this.address = address; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + } + + public static class Foo { + } +} diff --git a/spring-integration-voldemort/src/test/resources/cluster.xml b/spring-integration-voldemort/src/test/resources/cluster.xml new file mode 100755 index 0000000..69f4a64 --- /dev/null +++ b/spring-integration-voldemort/src/test/resources/cluster.xml @@ -0,0 +1,12 @@ + + + test-cluster + + 0 + localhost + 8080 + 6666 + 6667 + 0, 1 + + \ No newline at end of file diff --git a/spring-integration-voldemort/src/test/resources/log4j.properties b/spring-integration-voldemort/src/test/resources/log4j.properties new file mode 100755 index 0000000..4130143 --- /dev/null +++ b/spring-integration-voldemort/src/test/resources/log4j.properties @@ -0,0 +1,8 @@ +log4j.rootCategory=WARN, stdout + +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss.SSS} %-5p [%t][%c] %m%n + +log4j.category.org.springframework.integration=WARN +log4j.category.org.springframework.integration.voldemort=INFO diff --git a/spring-integration-voldemort/src/test/resources/org/springframework/integration/voldemort/test/common-test-context.xml b/spring-integration-voldemort/src/test/resources/org/springframework/integration/voldemort/test/common-test-context.xml new file mode 100755 index 0000000..2995ee6 --- /dev/null +++ b/spring-integration-voldemort/src/test/resources/org/springframework/integration/voldemort/test/common-test-context.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-integration-voldemort/src/test/resources/org/springframework/integration/voldemort/test/inbound/ObjectKeyTest-context.xml b/spring-integration-voldemort/src/test/resources/org/springframework/integration/voldemort/test/inbound/ObjectKeyTest-context.xml new file mode 100644 index 0000000..bda59a3 --- /dev/null +++ b/spring-integration-voldemort/src/test/resources/org/springframework/integration/voldemort/test/inbound/ObjectKeyTest-context.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-integration-voldemort/src/test/resources/org/springframework/integration/voldemort/test/inbound/VoldemortInboundAdapterTest-context.xml b/spring-integration-voldemort/src/test/resources/org/springframework/integration/voldemort/test/inbound/VoldemortInboundAdapterTest-context.xml new file mode 100644 index 0000000..f5ac708 --- /dev/null +++ b/spring-integration-voldemort/src/test/resources/org/springframework/integration/voldemort/test/inbound/VoldemortInboundAdapterTest-context.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-integration-voldemort/src/test/resources/org/springframework/integration/voldemort/test/outbound/ObjectKeyTest-context.xml b/spring-integration-voldemort/src/test/resources/org/springframework/integration/voldemort/test/outbound/ObjectKeyTest-context.xml new file mode 100644 index 0000000..a0e7eb6 --- /dev/null +++ b/spring-integration-voldemort/src/test/resources/org/springframework/integration/voldemort/test/outbound/ObjectKeyTest-context.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-integration-voldemort/src/test/resources/org/springframework/integration/voldemort/test/outbound/VoldemortOutboundAdapterTest-context.xml b/spring-integration-voldemort/src/test/resources/org/springframework/integration/voldemort/test/outbound/VoldemortOutboundAdapterTest-context.xml new file mode 100755 index 0000000..1c0b337 --- /dev/null +++ b/spring-integration-voldemort/src/test/resources/org/springframework/integration/voldemort/test/outbound/VoldemortOutboundAdapterTest-context.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-integration-voldemort/src/test/resources/org/springframework/integration/voldemort/test/store/VoldemortMessageStoreAggregationTest-context.xml b/spring-integration-voldemort/src/test/resources/org/springframework/integration/voldemort/test/store/VoldemortMessageStoreAggregationTest-context.xml new file mode 100644 index 0000000..c861068 --- /dev/null +++ b/spring-integration-voldemort/src/test/resources/org/springframework/integration/voldemort/test/store/VoldemortMessageStoreAggregationTest-context.xml @@ -0,0 +1,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/spring-integration-voldemort/src/test/resources/org/springframework/integration/voldemort/test/store/store-test-context.xml b/spring-integration-voldemort/src/test/resources/org/springframework/integration/voldemort/test/store/store-test-context.xml new file mode 100644 index 0000000..83f1ba9 --- /dev/null +++ b/spring-integration-voldemort/src/test/resources/org/springframework/integration/voldemort/test/store/store-test-context.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-integration-voldemort/src/test/resources/org/springframework/integration/voldemort/test/store/stores.xml b/spring-integration-voldemort/src/test/resources/org/springframework/integration/voldemort/test/store/stores.xml new file mode 100644 index 0000000..afa6fbf --- /dev/null +++ b/spring-integration-voldemort/src/test/resources/org/springframework/integration/voldemort/test/store/stores.xml @@ -0,0 +1,19 @@ + + + + test-message-store + lukasz.antoniak@gmail.com + bdb + client + 1 + 1 + 1 + + string + UTF-8 + + + java-serialization + + + \ No newline at end of file diff --git a/spring-integration-voldemort/src/test/resources/stores.xml b/spring-integration-voldemort/src/test/resources/stores.xml new file mode 100755 index 0000000..013c15f --- /dev/null +++ b/spring-integration-voldemort/src/test/resources/stores.xml @@ -0,0 +1,34 @@ + + + + test-store + lukasz.antoniak@gmail.com + bdb + client + 1 + 1 + 1 + + string + UTF-8 + + + java-serialization + + + + test-object-store + lukasz.antoniak@gmail.com + bdb + client + 1 + 1 + 1 + + java-serialization + + + java-serialization + + + \ No newline at end of file