From 745dd018ca58f8b4ffb06f6306a23a82afd7d1a0 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Thu, 18 Apr 2013 18:11:56 -0400 Subject: [PATCH 1/2] INTEXT-64 MQTT Adapters See README.md for more information. http://jira.springsource.org/browse/INTEXT-64 --- spring-integration-mqtt/README.md | 76 +++++ spring-integration-mqtt/build.gradle | 266 ++++++++++++++++++ spring-integration-mqtt/gradle.properties | 1 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 39770 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + spring-integration-mqtt/gradlew | 164 +++++++++++ spring-integration-mqtt/gradlew.bat | 90 ++++++ spring-integration-mqtt/publish-maven.gradle | 61 ++++ spring-integration-mqtt/src/api/overview.html | 22 ++ .../src/dist/changelog.txt | 15 + spring-integration-mqtt/src/dist/license.txt | 201 +++++++++++++ spring-integration-mqtt/src/dist/notice.txt | 21 ++ spring-integration-mqtt/src/dist/readme.txt | 13 + ...MqttMessageDrivenChannelAdapterParser.java | 48 ++++ .../mqtt/config/xml/MqttNamespaceHandler.java | 36 +++ .../xml/MqttOutboundChannelAdapterParser.java | 66 +++++ .../mqtt/config/xml/MqttParserUtils.java | 53 ++++ .../mqtt/config/xml/package-info.java | 4 + .../core/DefaultMqttPahoClientFactory.java | 159 +++++++++++ .../mqtt/core/MqttPahoClientFactory.java | 32 +++ .../integration/mqtt/core/package-info.java | 4 + ...stractMqttMessageDrivenChannelAdapter.java | 84 ++++++ .../MqttPahoMessageDrivenChannelAdapter.java | 156 ++++++++++ .../mqtt/inbound/package-info.java | 4 + .../outbound/AbstractMqttMessageHandler.java | 158 +++++++++++ .../mqtt/outbound/MqttPahoMessageHandler.java | 117 ++++++++ .../mqtt/outbound/package-info.java | 4 + .../integration/mqtt/package-info.java | 4 + .../support/DefaultPahoMessageConverter.java | 103 +++++++ .../integration/mqtt/support/MqttHeaders.java | 38 +++ .../mqtt/support/MqttMessageConverter.java | 31 ++ .../integration/mqtt/support/MqttUtils.java | 33 +++ .../mqtt/support/package-info.java | 4 + .../main/resources/META-INF/spring.handlers | 1 + .../main/resources/META-INF/spring.schemas | 2 + .../main/resources/META-INF/spring.tooling | 4 + .../xml/spring-integration-mqtt-1.0.xsd | 200 +++++++++++++ .../xml/spring-integration-mqttadapter.gif | Bin 0 -> 572 bytes .../docbook/SIAdapterLowerPrefix.xml | 73 +++++ .../src/reference/docbook/history.xml | 8 + .../src/reference/docbook/images/logo.png | Bin 0 -> 17393 bytes .../src/reference/docbook/index.xml | 68 +++++ .../src/reference/docbook/resources.xml | 17 ++ .../src/reference/docbook/whats-new.xml | 11 + .../mqtt/BackTobackAdapterTests.java | 94 +++++++ .../integration/mqtt/BrokerRunning.java | 82 ++++++ .../integration/mqtt/MqttAdapterTests.java | 237 ++++++++++++++++ ...rivenChannelAdapterParserTests-context.xml | 40 +++ ...essageDrivenChannelAdapterParserTests.java | 84 ++++++ ...boundChannelAdapterParserTests-context.xml | 39 +++ ...MqttOutboundChannelAdapterParserTests.java | 84 ++++++ .../src/test/resources/log4j.properties | 8 + 52 files changed, 3126 insertions(+) create mode 100644 spring-integration-mqtt/README.md create mode 100644 spring-integration-mqtt/build.gradle create mode 100644 spring-integration-mqtt/gradle.properties create mode 100644 spring-integration-mqtt/gradle/wrapper/gradle-wrapper.jar create mode 100644 spring-integration-mqtt/gradle/wrapper/gradle-wrapper.properties create mode 100755 spring-integration-mqtt/gradlew create mode 100644 spring-integration-mqtt/gradlew.bat create mode 100644 spring-integration-mqtt/publish-maven.gradle create mode 100644 spring-integration-mqtt/src/api/overview.html create mode 100644 spring-integration-mqtt/src/dist/changelog.txt create mode 100644 spring-integration-mqtt/src/dist/license.txt create mode 100644 spring-integration-mqtt/src/dist/notice.txt create mode 100644 spring-integration-mqtt/src/dist/readme.txt create mode 100644 spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/config/xml/MqttMessageDrivenChannelAdapterParser.java create mode 100644 spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/config/xml/MqttNamespaceHandler.java create mode 100644 spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/config/xml/MqttOutboundChannelAdapterParser.java create mode 100644 spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/config/xml/MqttParserUtils.java create mode 100644 spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/config/xml/package-info.java create mode 100644 spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/core/DefaultMqttPahoClientFactory.java create mode 100644 spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/core/MqttPahoClientFactory.java create mode 100644 spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/core/package-info.java create mode 100644 spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/inbound/AbstractMqttMessageDrivenChannelAdapter.java create mode 100644 spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/inbound/MqttPahoMessageDrivenChannelAdapter.java create mode 100644 spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/inbound/package-info.java create mode 100644 spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/outbound/AbstractMqttMessageHandler.java create mode 100644 spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/outbound/MqttPahoMessageHandler.java create mode 100644 spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/outbound/package-info.java create mode 100644 spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/package-info.java create mode 100644 spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/support/DefaultPahoMessageConverter.java create mode 100644 spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/support/MqttHeaders.java create mode 100644 spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/support/MqttMessageConverter.java create mode 100644 spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/support/MqttUtils.java create mode 100644 spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/support/package-info.java create mode 100644 spring-integration-mqtt/src/main/resources/META-INF/spring.handlers create mode 100644 spring-integration-mqtt/src/main/resources/META-INF/spring.schemas create mode 100644 spring-integration-mqtt/src/main/resources/META-INF/spring.tooling create mode 100644 spring-integration-mqtt/src/main/resources/org/springframework/integration/mqtt/config/xml/spring-integration-mqtt-1.0.xsd create mode 100644 spring-integration-mqtt/src/main/resources/org/springframework/integration/mqtt/config/xml/spring-integration-mqttadapter.gif create mode 100644 spring-integration-mqtt/src/reference/docbook/SIAdapterLowerPrefix.xml create mode 100644 spring-integration-mqtt/src/reference/docbook/history.xml create mode 100644 spring-integration-mqtt/src/reference/docbook/images/logo.png create mode 100644 spring-integration-mqtt/src/reference/docbook/index.xml create mode 100644 spring-integration-mqtt/src/reference/docbook/resources.xml create mode 100644 spring-integration-mqtt/src/reference/docbook/whats-new.xml create mode 100644 spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/BackTobackAdapterTests.java create mode 100644 spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/BrokerRunning.java create mode 100644 spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/MqttAdapterTests.java create mode 100644 spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/config/xml/MqttMessageDrivenChannelAdapterParserTests-context.xml create mode 100644 spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/config/xml/MqttMessageDrivenChannelAdapterParserTests.java create mode 100644 spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/config/xml/MqttOutboundChannelAdapterParserTests-context.xml create mode 100644 spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/config/xml/MqttOutboundChannelAdapterParserTests.java create mode 100644 spring-integration-mqtt/src/test/resources/log4j.properties diff --git a/spring-integration-mqtt/README.md b/spring-integration-mqtt/README.md new file mode 100644 index 0000000..1a49cf9 --- /dev/null +++ b/spring-integration-mqtt/README.md @@ -0,0 +1,76 @@ +Spring Integration Mqtt Adapters +================================================= + +`inbound` and `outbound` channel adapters are provided for Mqtt. The current implementation uses the [Eclipse Paho][] client. + +Example configurations... + + + + + + +Spring integration messages sent to the outbound adapter can have headers `mqtt_topic, mqtt_qos, mqtt_retained` which will override the defaults configured on the adapter. + +Inbound messages will have headers + + mqtt_topic - the topic from which the message was received + mqtt_duplicate - true if the message is a duplicate + mqtt_qos - the quality of service + + + +Both adapters use a `MqttPahoClientFactory` to get a client instance; the same factory also provides connection options from configured properties (such as user/password). The client factory bean (`DefaultMqttPahoClientFactory`) is provided to the adapter using the `client-factory` attribute. When not provided, a default factory instance is used. + + +Currently tested with the RabbitMQ MQTT plugin. + + +##Note: + +Currently, the Paho java client is not mavenized; there is an [open paho bug][] to resolve this. In the meantime, you can manually add the jar to your maven repo: + + mvn install:install-file -DgroupId=org.eclipse.paho -DartifactId=MQTT-Java -Dversion=3.0 -Dpackaging=jar -Dfile=/path/to/org.eclipse.paho.client.mqttv3.jar + + + +Check out the [Spring Integration forums][] and the [spring-integration][spring-integration tag] tag +on [Stack Overflow][]. [Commercial support][] is available, too. + +## Related GitHub projects + +* [Spring Integration][] +* [Spring Integration Samples][] +* [Spring Integration Templates][] +* [Spring Integration Dsl Groovy][] +* [Spring Integration Dsl Scala][] +* [Spring Integration Pattern Catalog][] + +For more information, please also don't forget to visit the [Spring Integration][] website. + +## Eclipse Paho + +* [Eclipse Paho][] + +[Spring Integration]: https://github.com/SpringSource/spring-integration +[Commercial support]: http://springsource.com/support/springsupport +[Spring Integration forums]: http://forum.springsource.org/forumdisplay.php?42-Integration +[spring-integration tag]: http://stackoverflow.com/questions/tagged/spring-integration +[Spring Integration Samples]: https://github.com/SpringSource/spring-integration-samples +[Spring Integration Templates]: https://github.com/SpringSource/spring-integration-templates/tree/master/si-sts-templates +[Spring Integration Dsl Groovy]: https://github.com/SpringSource/spring-integration-dsl-groovy +[Spring Integration Dsl Scala]: https://github.com/SpringSource/spring-integration-dsl-scala +[Spring Integration Pattern Catalog]: https://github.com/SpringSource/spring-integration-pattern-catalog +[Stack Overflow]: http://stackoverflow.com/faq +[Eclipse Paho]: http://www.eclipse.org/paho/ +[open paho bug]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=382471 \ No newline at end of file diff --git a/spring-integration-mqtt/build.gradle b/spring-integration-mqtt/build.gradle new file mode 100644 index 0000000..7e53dda --- /dev/null +++ b/spring-integration-mqtt/build.gradle @@ -0,0 +1,266 @@ +description = 'Spring Integration Mqtt 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.mqtt' + +repositories { + maven { url 'http://repo.springsource.org/libs-snapshot' } + maven { url 'http://repo.springsource.org/plugins-release' } + mavenLocal() +} + +sourceCompatibility=1.6 +targetCompatibility=1.6 + +ext { + junitVersion = '4.10' + log4jVersion = '1.2.12' + mockitoVersion = '1.9.0' + springVersion = '3.1.3.RELEASE' + springIntegrationVersion = '3.0.0.BUILD-SNAPSHOT' + + idPrefix = 'mqttadapter' +} + +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 "org.eclipse.paho:MQTT-Java:3.0" + 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) + jvmArgs "-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(); + def shortName = idPrefix.replaceFirst("${idPrefix}-", '') + + 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-mqtt/gradle.properties b/spring-integration-mqtt/gradle.properties new file mode 100644 index 0000000..bebfcbc --- /dev/null +++ b/spring-integration-mqtt/gradle.properties @@ -0,0 +1 @@ +version=1.0.0.BUILD-SNAPSHOT diff --git a/spring-integration-mqtt/gradle/wrapper/gradle-wrapper.jar b/spring-integration-mqtt/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..81dcde627d5cfbc12c96dc5113aac79b66a15118 GIT binary patch literal 39770 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 \(.*\)$'` + 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-mqtt/gradlew.bat b/spring-integration-mqtt/gradlew.bat new file mode 100644 index 0000000..8a0b282 --- /dev/null +++ b/spring-integration-mqtt/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-mqtt/publish-maven.gradle b/spring-integration-mqtt/publish-maven.gradle new file mode 100644 index 0000000..113adda --- /dev/null +++ b/spring-integration-mqtt/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 = 'not specified' + name = 'Gary Russell' + email = 'not specified' + } + } + } + } +} diff --git a/spring-integration-mqtt/src/api/overview.html b/spring-integration-mqtt/src/api/overview.html new file mode 100644 index 0000000..fb0198b --- /dev/null +++ b/spring-integration-mqtt/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-mqtt/src/dist/changelog.txt b/spring-integration-mqtt/src/dist/changelog.txt new file mode 100644 index 0000000..672b7ef --- /dev/null +++ b/spring-integration-mqtt/src/dist/changelog.txt @@ -0,0 +1,15 @@ +Spring Integration MqttAdapter Adapter CHANGELOG +========================================= + +For the full detailed changelog, see: +https://.... + + +Changes in version 1.0 GA (insert date here) +https://.... + + +*** GENERAL *** + +Upgraded Spring Framework dependency to ... +... diff --git a/spring-integration-mqtt/src/dist/license.txt b/spring-integration-mqtt/src/dist/license.txt new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/spring-integration-mqtt/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-mqtt/src/dist/notice.txt b/spring-integration-mqtt/src/dist/notice.txt new file mode 100644 index 0000000..f62045a --- /dev/null +++ b/spring-integration-mqtt/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-mqtt/src/dist/readme.txt b/spring-integration-mqtt/src/dist/readme.txt new file mode 100644 index 0000000..de9fd7e --- /dev/null +++ b/spring-integration-mqtt/src/dist/readme.txt @@ -0,0 +1,13 @@ +Spring Integration Mqtt Adapters +----------------------------------- + +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-mqtt/src/main/java/org/springframework/integration/mqtt/config/xml/MqttMessageDrivenChannelAdapterParser.java b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/config/xml/MqttMessageDrivenChannelAdapterParser.java new file mode 100644 index 0000000..55b0717 --- /dev/null +++ b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/config/xml/MqttMessageDrivenChannelAdapterParser.java @@ -0,0 +1,48 @@ +/* + * 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.mqtt.config.xml; + +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.config.xml.AbstractChannelAdapterParser; +import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter; +import org.w3c.dom.Element; + +/** + * The MqttAdapter Message Driven Channel adapter parser + * + * @author Gary Russell + * @since 1.0 + * + */ +public class MqttMessageDrivenChannelAdapterParser extends AbstractChannelAdapterParser { + + + @Override + protected AbstractBeanDefinition doParse(Element element, ParserContext parserContext, String channelName) { + + BeanDefinitionBuilder builder = BeanDefinitionBuilder + .genericBeanDefinition(MqttPahoMessageDrivenChannelAdapter.class); + + MqttParserUtils.parseCommon(element, builder); + builder.addConstructorArgValue(element.getAttribute("topics")); + builder.addPropertyReference("outputChannel", channelName); + + return builder.getBeanDefinition(); + } + +} diff --git a/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/config/xml/MqttNamespaceHandler.java b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/config/xml/MqttNamespaceHandler.java new file mode 100644 index 0000000..ea99d71 --- /dev/null +++ b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/config/xml/MqttNamespaceHandler.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.mqtt.config.xml; + +import org.springframework.integration.config.xml.AbstractIntegrationNamespaceHandler; + +/** + * The namespace handler for the MqttAdapter namespace + * + * @author Gary Russell + * @since 1.0 + * + */ +public class MqttNamespaceHandler extends AbstractIntegrationNamespaceHandler { + + /* (non-Javadoc) + * @see org.springframework.beans.factory.xml.NamespaceHandler#init() + */ + public void init() { + this.registerBeanDefinitionParser("message-driven-channel-adapter", new MqttMessageDrivenChannelAdapterParser()); + this.registerBeanDefinitionParser("outbound-channel-adapter", new MqttOutboundChannelAdapterParser()); + } +} diff --git a/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/config/xml/MqttOutboundChannelAdapterParser.java b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/config/xml/MqttOutboundChannelAdapterParser.java new file mode 100644 index 0000000..e9635c8 --- /dev/null +++ b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/config/xml/MqttOutboundChannelAdapterParser.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.mqtt.config.xml; + +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler; +import org.springframework.util.StringUtils; +import org.w3c.dom.Element; + +/** + * The parser for the MqttAdapter Outbound Channel Adapter. + * + * @author Gary Russell + * @since 1.0 + * + */ +public class MqttOutboundChannelAdapterParser extends AbstractOutboundChannelAdapterParser { + + @Override + protected boolean shouldGenerateId() { + return false; + } + + @Override + protected boolean shouldGenerateIdAsFallback() { + return true; + } + + @Override + protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) { + + final BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MqttPahoMessageHandler.class); + + MqttParserUtils.parseCommon(element, builder); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "default-topic"); + if (StringUtils.hasText(element.getAttribute("converter")) && + (StringUtils.hasText(element.getAttribute("default-qos")) || + StringUtils.hasText(element.getAttribute("default-retained")))) { + parserContext.getReaderContext().error("If a 'converter' is provided, you cannot provide " + + "'default-qos' or 'default-retained'", element); + } + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "default-qos"); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "default-retained"); + + return builder.getBeanDefinition(); + + } + +} diff --git a/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/config/xml/MqttParserUtils.java b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/config/xml/MqttParserUtils.java new file mode 100644 index 0000000..123cf5f --- /dev/null +++ b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/config/xml/MqttParserUtils.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.mqtt.config.xml; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.springframework.util.StringUtils; +import org.w3c.dom.Element; + +/** + * Contains various utility methods for parsing Mqtt Adapter + * specific namesspace elements as well as for the generation of the the + * respective {@link BeanDefinition}s. + * + * @author Gary Russell + * @since 1.0 + * + */ +public final class MqttParserUtils { + + /** Prevent instantiation. */ + private MqttParserUtils() { + throw new AssertionError(); + } + + public static void parseCommon(Element element, BeanDefinitionBuilder builder) { + builder.addConstructorArgValue(element.getAttribute("url")); + builder.addConstructorArgValue(element.getAttribute("client-id")); + String clientFactory = element.getAttribute("client-factory"); + if (StringUtils.hasText(clientFactory)) { + builder.addConstructorArgReference(clientFactory); + } + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "converter"); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "auto-startup"); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "phase"); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "send-timeout"); + } + +} diff --git a/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/config/xml/package-info.java b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/config/xml/package-info.java new file mode 100644 index 0000000..9c047f7 --- /dev/null +++ b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/config/xml/package-info.java @@ -0,0 +1,4 @@ +/** + * Provides parser classes to provide Xml namespace support for the MqttAdapter components. + */ +package org.springframework.integration.mqtt.config.xml; diff --git a/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/core/DefaultMqttPahoClientFactory.java b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/core/DefaultMqttPahoClientFactory.java new file mode 100644 index 0000000..1eda5d8 --- /dev/null +++ b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/core/DefaultMqttPahoClientFactory.java @@ -0,0 +1,159 @@ +/* + * 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.mqtt.core; + +import java.util.Properties; + +import javax.net.SocketFactory; + +import org.eclipse.paho.client.mqttv3.MqttClient; +import org.eclipse.paho.client.mqttv3.MqttClientPersistence; +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; +import org.eclipse.paho.client.mqttv3.MqttException; + +/** + * Creates a default {@link MqttClient} and a set of options as configured. + * @author Gary Russell + * @since 1.0 + * + */ +public class DefaultMqttPahoClientFactory implements MqttPahoClientFactory { + + private volatile Boolean cleanSession; + + private volatile Integer connectionTimeout; + + private volatile Integer keepAliveInterval; + + private volatile String password; + + private volatile SocketFactory socketFactory; + + private volatile Properties sslProperties; + + private volatile String userName; + + private volatile MqttClientPersistence persistence; + + private volatile Will will; + + public void setCleanSession(Boolean cleanSession) { + this.cleanSession = cleanSession; + } + + public void setConnectionTimeout(Integer connectionTimeout) { + this.connectionTimeout = connectionTimeout; + } + + public void setKeepAliveInterval(Integer keepAliveInterval) { + this.keepAliveInterval = keepAliveInterval; + } + + public void setPassword(String password) { + this.password = password; + } + + public void setSocketFactory(SocketFactory socketFactory) { + this.socketFactory = socketFactory; + } + + public void setSslProperties(Properties sslProperties) { + this.sslProperties = sslProperties; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public void setWill(Will will) { + this.will = will; + } + + public void setPersistence(MqttClientPersistence persistence) { + this.persistence = persistence; + } + + @Override + public MqttClient getClientInstance(String url, String clientId) throws MqttException { + return new MqttClient(url, clientId, this.persistence); + } + + @Override + public MqttConnectOptions getConnectionOptions() { + MqttConnectOptions options = new MqttConnectOptions(); + if (this.cleanSession != null) { + options.setCleanSession(this.cleanSession); + } + if (this.connectionTimeout != null) { + options.setConnectionTimeout(this.connectionTimeout); + } + if (this.keepAliveInterval != null) { + options.setKeepAliveInterval(this.keepAliveInterval); + } + if (this.password != null) { + options.setPassword(this.password.toCharArray()); + } + if (this.socketFactory != null) { + options.setSocketFactory(this.socketFactory); + } + if (this.sslProperties != null) { + options.setSSLProperties(this.sslProperties); + } + if (this.userName != null) { + options.setUserName(this.userName); + } + if (this.will != null) { + options.setWill(this.will.getTopic(), this.will.getPayload(), this.will.getQos(), this.will.isRetained()); + } + return options; + } + + public static class Will { + + private final String topic; + + private final byte[] payload; + + private final int qos; + + private final boolean retained; + + public Will(String topic, byte[] payload, int qos, boolean retained) { + this.topic = topic; + this.payload = payload; + this.qos = qos; + this.retained = retained; + } + + protected String getTopic() { + return topic; + } + + protected byte[] getPayload() { + return payload; + } + + protected int getQos() { + return qos; + } + + protected boolean isRetained() { + return retained; + } + + } + +} diff --git a/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/core/MqttPahoClientFactory.java b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/core/MqttPahoClientFactory.java new file mode 100644 index 0000000..d7dcf74 --- /dev/null +++ b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/core/MqttPahoClientFactory.java @@ -0,0 +1,32 @@ +/* + * 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.mqtt.core; + +import org.eclipse.paho.client.mqttv3.MqttClient; +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; +import org.eclipse.paho.client.mqttv3.MqttException; + +/** + * @author Gary Russell + * @since 1.0 + * + */ +public interface MqttPahoClientFactory { + + MqttClient getClientInstance(String url, String clientId) throws MqttException; + + MqttConnectOptions getConnectionOptions(); +} diff --git a/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/core/package-info.java b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/core/package-info.java new file mode 100644 index 0000000..1488613 --- /dev/null +++ b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/core/package-info.java @@ -0,0 +1,4 @@ +/** + * Provides core classes of the MqttAdapter module. + */ +package org.springframework.integration.mqtt.core; diff --git a/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/inbound/AbstractMqttMessageDrivenChannelAdapter.java b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/inbound/AbstractMqttMessageDrivenChannelAdapter.java new file mode 100644 index 0000000..0806980 --- /dev/null +++ b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/inbound/AbstractMqttMessageDrivenChannelAdapter.java @@ -0,0 +1,84 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.integration.mqtt.inbound; + +import org.springframework.integration.endpoint.MessageProducerSupport; +import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter; +import org.springframework.integration.mqtt.support.MqttMessageConverter; +import org.springframework.util.Assert; + +/** + * Abstract class for MQTT Message-Driven Channel Adapters. + * @author Gary Russell + * @since 1.0 + * + */ +public abstract class AbstractMqttMessageDrivenChannelAdapter extends MessageProducerSupport { + + private final String url; + + private final String clientId; + + private final String[] topic; + + private volatile MqttMessageConverter converter; + + public AbstractMqttMessageDrivenChannelAdapter(String url, String clientId, String... topic) { + Assert.hasText(url, "'url' cannot be null or empty"); + Assert.hasText(clientId, "'clientId' cannot be null or empty"); + Assert.notNull(topic, "'topics' cannot be null"); + Assert.isTrue(topic.length > 0, "'topics' cannot be empty"); + Assert.noNullElements(topic, "'topics' cannot have null elements"); + this.url = url; + this.clientId = clientId; + this.topic = topic; + } + + public void setConverter(MqttMessageConverter converter) { + Assert.notNull(converter, "'converter' cannot be null"); + this.converter = converter; + } + + protected String getUrl() { + return url; + } + + protected String getClientId() { + return clientId; + } + + protected MqttMessageConverter getConverter() { + return converter; + } + + protected String[] getTopic() { + return topic; + } + + @Override + protected void onInit() { + super.onInit(); + if (this.converter == null) { + this.converter = new DefaultPahoMessageConverter(); + } + } + + @Override + public String getComponentType(){ + return "mqtt:inbound-channel-adapter"; + } + +} diff --git a/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/inbound/MqttPahoMessageDrivenChannelAdapter.java b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/inbound/MqttPahoMessageDrivenChannelAdapter.java new file mode 100644 index 0000000..36876c9 --- /dev/null +++ b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/inbound/MqttPahoMessageDrivenChannelAdapter.java @@ -0,0 +1,156 @@ +/* + * 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.mqtt.inbound; + +import java.util.concurrent.ScheduledFuture; + +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; +import org.eclipse.paho.client.mqttv3.MqttCallback; +import org.eclipse.paho.client.mqttv3.MqttClient; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttMessage; +import org.springframework.integration.Message; +import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory; +import org.springframework.integration.mqtt.core.MqttPahoClientFactory; + +/** + * Eclipse Paho Implementation. + * + * @author Gary Russell + * @since 1.0 + * + */ +public class MqttPahoMessageDrivenChannelAdapter extends AbstractMqttMessageDrivenChannelAdapter + implements MqttCallback { + + private final MqttPahoClientFactory clientFactory; + + private volatile MqttClient client; + + private volatile ScheduledFuture reconnectFuture; + + private volatile boolean connected; + + + public MqttPahoMessageDrivenChannelAdapter(String url, String clientId, MqttPahoClientFactory clientFactory, + String... topic) { + super(url, clientId, topic); + this.clientFactory = clientFactory; + } + + public MqttPahoMessageDrivenChannelAdapter(String url, String clientId, String... topic) { + this(url, clientId, new DefaultMqttPahoClientFactory(), topic); + } + + @Override + protected void doStart() { + super.doStart(); + try { + this.connectAndSubscribe(); + } + catch (Exception e) { + logger.error("Exception while connecting and subscribing, retrying", e); + this.scheduleReconnect(); + } + } + + @Override + protected void doStop() { + super.doStop(); + try { + this.client.unsubscribe(this.getTopic()); + this.client.disconnect(); + this.client.close(); + this.connected = false; + this.client = null; + } + catch (MqttException e) { + logger.error("Exception while unsubscribing and disconnecting", e); + } + } + + private void connectAndSubscribe() throws MqttException { + this.client = this.clientFactory.getClientInstance(this.getUrl(), this.getClientId()); + this.client.connect(this.clientFactory.getConnectionOptions()); + try { + this.client.subscribe(this.getTopic()); + } + catch (MqttException e) { + this.client.disconnect(); + throw e; + } + if (this.client.isConnected()) { + this.client.setCallback(this); + this.connected = true; + if (this.reconnectFuture != null) { + this.cancelReconnect(); + } + if (logger.isDebugEnabled()) { + logger.debug("Connected and subscribed to " + this.getTopic()); + } + } + } + + private synchronized void cancelReconnect() { + if (this.reconnectFuture != null) { + this.reconnectFuture.cancel(false); + this.reconnectFuture = null; + } + } + + private void scheduleReconnect() { + try { + this.reconnectFuture = this.getTaskScheduler().scheduleWithFixedDelay(new Runnable() { + + @Override + public void run() { + try { + if (logger.isDebugEnabled()) { + logger.debug("Attempting reconnect"); + } + if (!connected) { + connectAndSubscribe(); + } + } + catch (MqttException e) { + logger.error("Exception while connecting and subscribing", e); + } + } + }, 10000); + } + catch (Exception e) { + logger.error("Failed to schedule reconnect", e); + } + } + + @Override + public void connectionLost(Throwable cause) { + this.logger.error("Lost connection:" + cause.getMessage() + "; retrying..."); + this.connected = false; + this.scheduleReconnect(); + } + + @Override + public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception { + Message message = this.getConverter().toMessage(topic, mqttMessage); + this.sendMessage(message); + } + + @Override + public void deliveryComplete(IMqttDeliveryToken token) { + } + +} diff --git a/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/inbound/package-info.java b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/inbound/package-info.java new file mode 100644 index 0000000..5dc35d5 --- /dev/null +++ b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/inbound/package-info.java @@ -0,0 +1,4 @@ +/** + * Provides inbound Spring Integration MqttAdapter components. + */ +package org.springframework.integration.mqtt.inbound; diff --git a/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/outbound/AbstractMqttMessageHandler.java b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/outbound/AbstractMqttMessageHandler.java new file mode 100644 index 0000000..12a4ef2 --- /dev/null +++ b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/outbound/AbstractMqttMessageHandler.java @@ -0,0 +1,158 @@ +/* + * 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.mqtt.outbound; + +import org.springframework.context.SmartLifecycle; +import org.springframework.integration.Message; +import org.springframework.integration.MessageHandlingException; +import org.springframework.integration.handler.AbstractMessageHandler; +import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter; +import org.springframework.integration.mqtt.support.MqttHeaders; +import org.springframework.integration.support.converter.MessageConverter; +import org.springframework.util.Assert; + +/** + * Abstract class for MQTT outbound channel adapters. + * @author Gary Russell + * @since 1.0 + * + */ +public abstract class AbstractMqttMessageHandler extends AbstractMessageHandler implements SmartLifecycle { + + private final String url; + + private final String clientId; + + private volatile String defaultTopic; + + private volatile int defaultQos = 0; + + private volatile boolean defaultRetained = false; + + private volatile MessageConverter converter; + + private boolean running; + + private volatile int phase; + + private volatile boolean autoStartup; + + public AbstractMqttMessageHandler(String url, String clientId) { + Assert.hasText(url, "'url' cannot be null or empty"); + Assert.hasText(clientId, "'clientId' cannot be null or empty"); + this.url = url; + this.clientId = clientId; + } + + public void setDefaultTopic(String defaultTopic) { + this.defaultTopic = defaultTopic; + } + + public void setDefaultQos(int defaultQos) { + this.defaultQos = defaultQos; + } + + public void setDefaultRetained(boolean defaultRetain) { + this.defaultRetained = defaultRetain; + } + + public void setConverter(MessageConverter converter) { + Assert.notNull(converter, "'converter' cannot be null"); + this.converter = converter; + } + + protected String getUrl() { + return url; + } + + protected String getClientId() { + return clientId; + } + + @Override + protected void onInit() throws Exception { + super.onInit(); + if (this.converter == null) { + this.converter = new DefaultPahoMessageConverter(this.defaultQos, this.defaultRetained); + } + } + + @Override + public final void start() { + this.doStart(); + } + + protected abstract void doStart(); + + @Override + public final void stop() { + this.doStop(); + } + + protected abstract void doStop(); + + @Override + public boolean isRunning() { + return this.running; + } + + @Override + public int getPhase() { + return this.phase; + } + + public void setPhase(int phase) { + this.phase = phase; + } + + public void setAutoStartup(boolean autoStartup) { + this.autoStartup = autoStartup; + } + + @Override + public boolean isAutoStartup() { + return this.autoStartup; + } + + @Override + public void stop(Runnable callback) { + this.stop(); + callback.run(); + } + + @Override + protected void handleMessageInternal(Message message) throws Exception { + this.connectIfNeeded(); + String topic = (String) message.getHeaders().get(MqttHeaders.TOPIC); + Object mqttMessage = this.converter.fromMessage(message); + if (topic == null && this.defaultTopic == null) { + throw new MessageHandlingException(message, + "No '" + MqttHeaders.TOPIC + "' header and no default topic defined"); + } + this.publish(topic == null ? this.defaultTopic : topic, mqttMessage); + } + + protected abstract void connectIfNeeded(); + + protected abstract void publish(String topic, Object mqttMessage) throws Exception; + + @Override + public String getComponentType() { + return "mqtt:outbound-channel-adapter"; + } + +} diff --git a/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/outbound/MqttPahoMessageHandler.java b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/outbound/MqttPahoMessageHandler.java new file mode 100644 index 0000000..810aae0 --- /dev/null +++ b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/outbound/MqttPahoMessageHandler.java @@ -0,0 +1,117 @@ +/* + * 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.mqtt.outbound; + +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; +import org.eclipse.paho.client.mqttv3.MqttCallback; +import org.eclipse.paho.client.mqttv3.MqttClient; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttMessage; +import org.springframework.integration.MessagingException; +import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory; +import org.springframework.integration.mqtt.core.MqttPahoClientFactory; +import org.springframework.util.Assert; + +/** + * Eclipse Paho implementation. + * @author Gary Russell + * @since 1.0 + * + */ +public class MqttPahoMessageHandler extends AbstractMqttMessageHandler + implements MqttCallback { + + private final MqttPahoClientFactory clientFactory; + + private volatile MqttClient client; + + public MqttPahoMessageHandler(String url, String clientId, MqttPahoClientFactory factory) { + super(url, clientId); + this.clientFactory = factory; + } + + public MqttPahoMessageHandler(String url, String clientId) { + this(url, clientId, new DefaultMqttPahoClientFactory()); + } + + @Override + protected void doStart() { + } + + @Override + protected void doStop() { + try { + if (this.client != null) { + this.client.disconnect(); + this.client.close(); + this.client = null; + } + } + catch (MqttException e) { + logger.error("Failed to disconnect", e); + } + } + + private synchronized void doConnect() throws MqttException { + if (this.client != null && !this.client.isConnected()) { + this.client.close(); + this.client = null; + } + if (this.client == null) { + this.client = this.clientFactory.getClientInstance(this.getUrl(), this.getClientId()); + this.client.connect(this.clientFactory.getConnectionOptions()); + this.client.setCallback(this); + if (logger.isDebugEnabled()) { + logger.debug("Client connected"); + } + } + } + + @Override + protected void connectIfNeeded() { + if (this.client == null || !this.client.isConnected()) { + try { + this.doConnect(); + } + catch (MqttException e) { + throw new MessagingException("Failed to connect", e); + } + } + } + + @Override + protected void publish(String topic, Object mqttMessage) throws Exception { + Assert.isInstanceOf(MqttMessage.class, mqttMessage); + this.client.publish(topic, (MqttMessage) mqttMessage); + } + + @Override + public void connectionLost(Throwable cause) { + logger.error("Lost connection; will attempt reconnect on next request"); + this.client = null; + } + + @Override + public void messageArrived(String topic, MqttMessage message) throws Exception { + + } + + @Override + public void deliveryComplete(IMqttDeliveryToken token) { + + } + +} diff --git a/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/outbound/package-info.java b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/outbound/package-info.java new file mode 100644 index 0000000..6b5d4e4 --- /dev/null +++ b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/outbound/package-info.java @@ -0,0 +1,4 @@ +/** + * Provides Spring Integration components for doing outbound operations. + */ +package org.springframework.integration.mqtt.outbound; diff --git a/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/package-info.java b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/package-info.java new file mode 100644 index 0000000..79304d7 --- /dev/null +++ b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/package-info.java @@ -0,0 +1,4 @@ +/** + * Root package of the MqttAdapter Module. + */ +package org.springframework.integration.mqtt; diff --git a/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/support/DefaultPahoMessageConverter.java b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/support/DefaultPahoMessageConverter.java new file mode 100644 index 0000000..20f8137 --- /dev/null +++ b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/support/DefaultPahoMessageConverter.java @@ -0,0 +1,103 @@ +/* + * 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.mqtt.support; + +import org.eclipse.paho.client.mqttv3.MqttMessage; +import org.springframework.integration.Message; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.integration.support.converter.MessageConversionException; +import org.springframework.util.Assert; + + +/** + * Default implementation allowing most connection options to be configured. + * @author Gary Russell + * @since 1.0 + * + */ +public class DefaultPahoMessageConverter implements MqttMessageConverter { + + private final String charset; + + private final Integer defaultQos; + + private final Boolean defaultRetained; + + public DefaultPahoMessageConverter() { + this (0, false); + } + + public DefaultPahoMessageConverter(int defaultQos, boolean defaultRetain) { + this(defaultQos, defaultRetain, "UTF-8"); + } + + public DefaultPahoMessageConverter(int defaultQos, boolean defaultRetained, String charset) { + this.defaultQos = defaultQos; + this.defaultRetained = defaultRetained; + this.charset = charset; + } + + @Override + @SuppressWarnings("unchecked") + public

Message

toMessage(Object object) { + return (Message

) toMessage(null, object); + } + + public Message toMessage(String topic, Object object) { + Assert.isInstanceOf(MqttMessage.class, object); + MqttMessage message = (MqttMessage) object; + try { + MessageBuilder messageBuilder = MessageBuilder.withPayload(new String(message.getPayload(), this.charset)) + .setHeader(MqttHeaders.QOS, message.getQos()) + .setHeader(MqttHeaders.DUPLICATE, message.isDuplicate()) + .setHeader(MqttHeaders.RETAINED, message.isRetained()); + if (topic != null) { + messageBuilder.setHeader(MqttHeaders.TOPIC, topic); + } + return messageBuilder.build(); + } + catch (Exception e) { + throw new MessageConversionException("failed to convert object to Message", e); + } + } + + @Override + public

Object fromMessage(Message

message) { + Object payload = message.getPayload(); + Assert.isTrue(payload instanceof byte[] || payload instanceof String); + byte[] payloadBytes; + if (payload instanceof String) { + try { + payloadBytes = ((String) payload).getBytes(this.charset); + } + catch (Exception e) { + throw new MessageConversionException("failed to convert Message to object", e); + } + } + else { + payloadBytes = (byte[]) payload; + } + MqttMessage mqttMessage = new MqttMessage(payloadBytes); + Object header = message.getHeaders().get(MqttHeaders.RETAINED); + Assert.isTrue(header == null || header instanceof Boolean, MqttHeaders.RETAINED + " header must be Boolean"); + mqttMessage.setRetained(header == null ? this.defaultRetained : (Boolean) header); + header = message.getHeaders().get(MqttHeaders.QOS); + Assert.isTrue(header == null || header instanceof Integer, MqttHeaders.QOS + " header must be Integer"); + mqttMessage.setQos(header == null ? this.defaultQos : (Integer) header); + return mqttMessage; + } + +} diff --git a/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/support/MqttHeaders.java b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/support/MqttHeaders.java new file mode 100644 index 0000000..b114ad6 --- /dev/null +++ b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/support/MqttHeaders.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.mqtt.support; + +/** + * Spring Integration headers. + * @author Gary Russell + * @since 3.0 + * + */ +public class MqttHeaders { + + private static final String prefix = "mqtt_"; + + public static final String QOS = prefix + "qos"; + + public static final String DUPLICATE = prefix + "duplicate"; + + public static final String RETAINED = prefix + "retained"; + + public static final String TOPIC = prefix + "topic"; + private MqttHeaders() { + throw new AssertionError(); + } +} diff --git a/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/support/MqttMessageConverter.java b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/support/MqttMessageConverter.java new file mode 100644 index 0000000..3d13741 --- /dev/null +++ b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/support/MqttMessageConverter.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.mqtt.support; + +import org.springframework.integration.Message; +import org.springframework.integration.support.converter.MessageConverter; + +/** + * Extension of {@link MessageConverter} allowing the topic to be added as + * a header. + * @author Gary Russell + * @since 1.0 + * + */ +public interface MqttMessageConverter extends MessageConverter { + + Message toMessage(String topic, Object object); +} diff --git a/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/support/MqttUtils.java b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/support/MqttUtils.java new file mode 100644 index 0000000..1f69d23 --- /dev/null +++ b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/support/MqttUtils.java @@ -0,0 +1,33 @@ +/* + * 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.mqtt.support; + + +/** + * Contains utility methods used by the MqttAdapter components. + * + * @author Gary Russell + * @since 1.0 + * + */ +public final class MqttUtils { + + /** Prevent instantiation. */ + private MqttUtils() { + throw new AssertionError(); + } + +} diff --git a/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/support/package-info.java b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/support/package-info.java new file mode 100644 index 0000000..c711af2 --- /dev/null +++ b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/support/package-info.java @@ -0,0 +1,4 @@ +/** + * Provides various support classes used across Spring Integration MqttAdapter Components. + */ +package org.springframework.integration.mqtt.support; diff --git a/spring-integration-mqtt/src/main/resources/META-INF/spring.handlers b/spring-integration-mqtt/src/main/resources/META-INF/spring.handlers new file mode 100644 index 0000000..fdbf583 --- /dev/null +++ b/spring-integration-mqtt/src/main/resources/META-INF/spring.handlers @@ -0,0 +1 @@ +http\://www.springframework.org/schema/integration/mqtt=org.springframework.integration.mqtt.config.xml.MqttNamespaceHandler diff --git a/spring-integration-mqtt/src/main/resources/META-INF/spring.schemas b/spring-integration-mqtt/src/main/resources/META-INF/spring.schemas new file mode 100644 index 0000000..f4273c5 --- /dev/null +++ b/spring-integration-mqtt/src/main/resources/META-INF/spring.schemas @@ -0,0 +1,2 @@ +http\://www.springframework.org/schema/integration/mqtt/spring-integration-mqtt-1.0.xsd=org/springframework/integration/mqtt/config/xml/spring-integration-mqtt-1.0.xsd +http\://www.springframework.org/schema/integration/mqtt/spring-integration-mqtt.xsd=org/springframework/integration/mqtt/config/xml/spring-integration-mqtt-1.0.xsd diff --git a/spring-integration-mqtt/src/main/resources/META-INF/spring.tooling b/spring-integration-mqtt/src/main/resources/META-INF/spring.tooling new file mode 100644 index 0000000..a062077 --- /dev/null +++ b/spring-integration-mqtt/src/main/resources/META-INF/spring.tooling @@ -0,0 +1,4 @@ +# Tooling related information for the integration MqttAdapter namespace +http\://www.springframework.org/schema/integration/mqttadapter@name=integration MqttAdapter Namespace +http\://www.springframework.org/schema/integration/mqttadapter@prefix=int-mqttadapter +http\://www.springframework.org/schema/integration/mqttadapter@icon=org/springframework/integration/config/xml/spring-integration-mqttadapter.gif diff --git a/spring-integration-mqtt/src/main/resources/org/springframework/integration/mqtt/config/xml/spring-integration-mqtt-1.0.xsd b/spring-integration-mqtt/src/main/resources/org/springframework/integration/mqtt/config/xml/spring-integration-mqtt-1.0.xsd new file mode 100644 index 0000000..ecf59a9 --- /dev/null +++ b/spring-integration-mqtt/src/main/resources/org/springframework/integration/mqtt/config/xml/spring-integration-mqtt-1.0.xsd @@ -0,0 +1,200 @@ + + + + + + + + + + + + + + + The definition for the Spring Integration MqttAdapter + Inbound Channel Adapter. + + + + + + + + + + + + + + + + + Specifies one or more (comma-delimited) topics on which to listen for messages. + + + + + + + + + + + + + + + + Defines an outbound Channel Adapter. + + + + + + + + + + + + Channel from which messages will be output. + When a message is sent to this channel it will + cause the query + to be executed. + + + + + + + + + + + Specifies the order for invocation when this endpoint is connected as a + subscriber to a SubscribableChannel. + + + + + + + Specifies the default topic to which messages will be sent. Required if an + outbound message does not have an 'mqtt_topic' header. + + + + + + + Specifies the default quality of service. Default 0. + + + + + + + Specifies the default value of the 'retained' flag. Default false. + + + + + + + + + + + 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'. + + + + + + + Flag to indicate that the component should start automatically + on startup (default true). + + + + + + + + + + Flag to indicate the phase in which the component should start automatically + on startup. See SmartLifecycle. + + + + + + + + + + MQTT broker URL. + + + + + + + MQTT client ID. + + + + + + + to/from + a paho MqttMessage. Default is DefaultMqttMessageConverter. + ]]> + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-mqtt/src/main/resources/org/springframework/integration/mqtt/config/xml/spring-integration-mqttadapter.gif b/spring-integration-mqtt/src/main/resources/org/springframework/integration/mqtt/config/xml/spring-integration-mqttadapter.gif new file mode 100644 index 0000000000000000000000000000000000000000..41b369fece0e576ecd920400b221fd33abbaf5c4 GIT binary patch literal 572 zcmZ?wbhEHb6krfwc*Xz%|NsBj^POrMHnn5wI!WKA$M#t>l?H4BPzO?b&{cRWSFFSB< z^|`x}P0QyVy|U`SiF40CoO%A?(2mZdyE;$q?mn}(`^J&Ji~DRU^KH0`_S=*f zu`(xWd1~172%niTfzv{QrUeE70}32q)Fc#tvM@3*q%i1!3doJ z$QZroAYKk;o)!}g_c$BZP}??kHkSD;%JZ2d6u9}BT8+&ujM`ePV%-d#6T`zi);F^_ zIR!9@O3I6^oV(z-tdN`(pX{#l&0dZgror~-%^VCYeSN$`0@iCL`334n#7Fs`w{uBO x(B)aWd{Qfm5=#n`xT2uIN}aG328D}QI)&6(d>(9QbkfS;6w=UeP!nLV1^^EL*)#wE literal 0 HcmV?d00001 diff --git a/spring-integration-mqtt/src/reference/docbook/SIAdapterLowerPrefix.xml b/spring-integration-mqtt/src/reference/docbook/SIAdapterLowerPrefix.xml new file mode 100644 index 0000000..151e09d --- /dev/null +++ b/spring-integration-mqtt/src/reference/docbook/SIAdapterLowerPrefix.xml @@ -0,0 +1,73 @@ + + + MqttAdapter Adapter + + The Spring Integration MqttAdapter Adapter provides... + + + + Outbound Channel adapter + + + Outbound Gateway + + + Inbound Channel Adapter + + + +

+ Java Implementation + Each of the provided components will use the + org.springframework.integration.mqtt.core.MqttAdapterExecutor + class... + +
+
+ Common Configuration Attributes + + Certain configuration parameters are shared amongst all MqttAdapter + components and are described below: + + + auto-startup + + Lifecycle attribute signaling if this component should + be started during Application Context startup. + Defaults to true. + Optional. + + + id + + Identifies the underlying Spring bean definition, which + is an instance of either EventDrivenConsumer + or PollingConsumer. + Optional. + + +
+ +
+ Outbound Channel Adapter + + The MqttAdapter Outbound channel adapter allows you to... + +
+
+ Outbound Gateway + + Outbound gateways are similar to outbound channel adapters except that it can also be used to + get a result on the reply channel after performing + the given... + +
+
+ Inbound Channel Adapter + + An inbound channel adapter is used to execute... + +
+ + diff --git a/spring-integration-mqtt/src/reference/docbook/history.xml b/spring-integration-mqtt/src/reference/docbook/history.xml new file mode 100644 index 0000000..549be21 --- /dev/null +++ b/spring-integration-mqtt/src/reference/docbook/history.xml @@ -0,0 +1,8 @@ + + + Change History + + + diff --git a/spring-integration-mqtt/src/reference/docbook/images/logo.png b/spring-integration-mqtt/src/reference/docbook/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..8c71c13a0e5606e9d87f75e37fe2874cb44beb51 GIT binary patch literal 17393 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 + + + Spring Integration MqttAdapter Adapter + MqttAdapter Adapter ${version} + Spring Integration + ${version} + + + + + + + + + + + + + + Gary Russell + + + © SpringSource Inc., 2012 + + + + + + + What's new? + + + For those who are already familiar with Spring Integration, this + chapter + provides a brief overview of the new features of version 2.2. If you are + interested in the changes and features, that were introduced in + earlier + versions, please take a look at chapter: + + + + + + + + + + Integration Adapters + + This section covers the various Channel Adapters and Messaging + Gateways provided + by Spring Integration to support Message-based communication with + external systems. + + + + + + Appendices + + Advanced Topics and Additional Resources + + + + diff --git a/spring-integration-mqtt/src/reference/docbook/resources.xml b/spring-integration-mqtt/src/reference/docbook/resources.xml new file mode 100644 index 0000000..109faa9 --- /dev/null +++ b/spring-integration-mqtt/src/reference/docbook/resources.xml @@ -0,0 +1,17 @@ + + + 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. + +
+ +
diff --git a/spring-integration-mqtt/src/reference/docbook/whats-new.xml b/spring-integration-mqtt/src/reference/docbook/whats-new.xml new file mode 100644 index 0000000..f037937 --- /dev/null +++ b/spring-integration-mqtt/src/reference/docbook/whats-new.xml @@ -0,0 +1,11 @@ + + + What's new? + + This chapter provides an overview of the new features and improvements + that have been added to the MqttAdapter Adapter: + + + diff --git a/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/BackTobackAdapterTests.java b/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/BackTobackAdapterTests.java new file mode 100644 index 0000000..60e44f3 --- /dev/null +++ b/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/BackTobackAdapterTests.java @@ -0,0 +1,94 @@ +/* + * 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.mqtt; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.junit.Rule; +import org.junit.Test; +import org.springframework.integration.Message; +import org.springframework.integration.channel.QueueChannel; +import org.springframework.integration.message.GenericMessage; +import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter; +import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler; +import org.springframework.integration.mqtt.support.MqttHeaders; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; + +/** + * @author Gary Russell + * @since 3.0 + * + */ +public class BackTobackAdapterTests { + + @Rule + public final BrokerRunning brokerRunning = BrokerRunning.isRunning(1883); + + @Test + public void testSingleTopic() { + MqttPahoMessageHandler adapter = new MqttPahoMessageHandler("tcp://localhost:1883", "si-test-out"); + adapter.setDefaultTopic("mqtt-foo"); + adapter.afterPropertiesSet(); + adapter.start(); + MqttPahoMessageDrivenChannelAdapter inbound = new MqttPahoMessageDrivenChannelAdapter("tcp://localhost:1883", "si-test-in", "mqtt-foo"); + QueueChannel outputChannel = new QueueChannel(); + inbound.setOutputChannel(outputChannel); + ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); + taskScheduler.initialize(); + inbound.setTaskScheduler(taskScheduler); + inbound.afterPropertiesSet(); + inbound.start(); + adapter.handleMessage(new GenericMessage("foo")); + adapter.stop(); + Message out = outputChannel.receive(1000); + assertNotNull(out); + inbound.stop(); + assertEquals("foo", out.getPayload()); + assertEquals("mqtt-foo", out.getHeaders().get(MqttHeaders.TOPIC)); + } + + @Test + public void testTwoTopics() { + MqttPahoMessageHandler adapter = new MqttPahoMessageHandler("tcp://localhost:1883", "si-test-out"); + adapter.setDefaultTopic("mqtt-foo"); + adapter.afterPropertiesSet(); + adapter.start(); + MqttPahoMessageDrivenChannelAdapter inbound = new MqttPahoMessageDrivenChannelAdapter("tcp://localhost:1883", "si-test-in", "mqtt-foo", "mqtt-bar"); + QueueChannel outputChannel = new QueueChannel(); + inbound.setOutputChannel(outputChannel); + ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); + taskScheduler.initialize(); + inbound.setTaskScheduler(taskScheduler); + inbound.afterPropertiesSet(); + inbound.start(); + adapter.handleMessage(new GenericMessage("foo")); + Message message = MessageBuilder.withPayload("bar").setHeader(MqttHeaders.TOPIC, "mqtt-bar").build(); + adapter.handleMessage(message); + adapter.stop(); + Message out = outputChannel.receive(1000); + assertNotNull(out); + inbound.stop(); + assertEquals("foo", out.getPayload()); + assertEquals("mqtt-foo", out.getHeaders().get(MqttHeaders.TOPIC)); + out = outputChannel.receive(1000); + assertNotNull(out); + inbound.stop(); + assertEquals("bar", out.getPayload()); + assertEquals("mqtt-bar", out.getHeaders().get(MqttHeaders.TOPIC)); } + +} diff --git a/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/BrokerRunning.java b/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/BrokerRunning.java new file mode 100644 index 0000000..f7c5726 --- /dev/null +++ b/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/BrokerRunning.java @@ -0,0 +1,82 @@ +/* + * 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.mqtt; + +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeTrue; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.paho.client.mqttv3.MqttClient; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; +import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory; + +/** + * @author Gary Russell + * @since 3.0 + * + */ +public class BrokerRunning extends TestWatcher { + + private static Log logger = LogFactory.getLog(BrokerRunning.class); + + // Static so that we only test once on failure: speeds up test suite + private static Map brokerOnline = new HashMap(); + + private final int port; + + private BrokerRunning(int port) { + this.port = port; + brokerOnline.put(port, true); + } + + @Override + public Statement apply(Statement base, Description description) { + assumeTrue(brokerOnline.get(port)); + String url = "tcp://localhost:" + port; + MqttClient client = null; + try { + client = new DefaultMqttPahoClientFactory().getClientInstance(url, "junit-" + System.currentTimeMillis()); + client.connect(); + } + catch (MqttException e) { + logger.warn("Tests not running because no broker on " + url + ":", e); + assumeNoException(e); + } + finally { + if (client != null) { + try { + client.close(); + } + catch (MqttException e) { + } + } + } + return super.apply(base, description); + } + + + + public static BrokerRunning isRunning(int port) { + return new BrokerRunning(port); + } +} diff --git a/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/MqttAdapterTests.java b/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/MqttAdapterTests.java new file mode 100644 index 0000000..bdce33f --- /dev/null +++ b/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/MqttAdapterTests.java @@ -0,0 +1,237 @@ +/* + * 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.mqtt; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Properties; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import javax.net.SocketFactory; + +import org.eclipse.paho.client.mqttv3.MqttCallback; +import org.eclipse.paho.client.mqttv3.MqttClient; +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; +import org.eclipse.paho.client.mqttv3.MqttMessage; +import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; +import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.springframework.integration.Message; +import org.springframework.integration.channel.QueueChannel; +import org.springframework.integration.message.GenericMessage; +import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory; +import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory.Will; +import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter; +import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; + +/** + * @author Gary Russell + * @since 3.0 + * + */ +public class MqttAdapterTests { + + @Test + public void testPahoConnectOptions() { + DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory(); + factory.setCleanSession(false); + factory.setConnectionTimeout(23); + factory.setKeepAliveInterval(45); + factory.setPassword("pass"); + SocketFactory socketFactory = mock(SocketFactory.class); + factory.setSocketFactory(socketFactory); + Properties props = new Properties(); + factory.setSslProperties(props); + factory.setUserName("user"); + Will will = new Will("foo", "bar".getBytes(), 2, true); + factory.setWill(will); + + MqttConnectOptions options = factory.getConnectionOptions(); + + assertEquals(23, options.getConnectionTimeout()); + assertEquals(45, options.getKeepAliveInterval()); + assertEquals("pass", new String(options.getPassword())); + assertSame(socketFactory, options.getSocketFactory()); + assertSame(props, options.getSSLProperties()); + assertEquals("user", options.getUserName()); + assertEquals("foo", options.getWillDestination()); + assertEquals("bar", new String(options.getWillMessage().getPayload())); + assertEquals(2, options.getWillMessage().getQos()); + + } + + @Test + public void testOutboundOptionsApplied() throws Exception { + DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory(); + factory.setCleanSession(false); + factory.setConnectionTimeout(23); + factory.setKeepAliveInterval(45); + factory.setPassword("pass"); + MemoryPersistence persistence = new MemoryPersistence(); + factory.setPersistence(persistence); + final SocketFactory socketFactory = mock(SocketFactory.class); + factory.setSocketFactory(socketFactory); + final Properties props = new Properties(); + factory.setSslProperties(props); + factory.setUserName("user"); + Will will = new Will("foo", "bar".getBytes(), 2, true); + factory.setWill(will); + + factory = spy(factory); + final MqttClient client = mock(MqttClient.class); + doAnswer(new Answer() { + + @Override + public MqttClient answer(InvocationOnMock invocation) throws Throwable { + return client; + } + }).when(factory).getClientInstance(anyString(), anyString()); + + MqttPahoMessageHandler handler = new MqttPahoMessageHandler("foo", "bar", factory); + handler.setDefaultTopic("mqtt-foo"); + handler.afterPropertiesSet(); + handler.start(); + final AtomicBoolean connectCalled = new AtomicBoolean(); + doAnswer(new Answer(){ + + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + MqttConnectOptions options = (MqttConnectOptions) invocation.getArguments()[0]; + assertEquals(23, options.getConnectionTimeout()); + assertEquals(45, options.getKeepAliveInterval()); + assertEquals("pass", new String(options.getPassword())); + assertSame(socketFactory, options.getSocketFactory()); + assertSame(props, options.getSSLProperties()); + assertEquals("user", options.getUserName()); + assertEquals("foo", options.getWillDestination()); + assertEquals("bar", new String(options.getWillMessage().getPayload())); + assertEquals(2, options.getWillMessage().getQos()); + connectCalled.set(true); + return null; + } + }).when(client).connect(any(MqttConnectOptions.class)); + final AtomicBoolean publishCalled = new AtomicBoolean(); + doAnswer(new Answer() { + + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + assertEquals("mqtt-foo", invocation.getArguments()[0]); + MqttMessage message = (MqttMessage) invocation.getArguments()[1]; + assertEquals("Hello, world!", new String(message.getPayload())); + publishCalled.set(true); + return null; + } + }).when(client).publish(anyString(), any(MqttMessage.class)); + + handler.handleMessage(new GenericMessage("Hello, world!")); + + verify(client, times(1)).connect(any(MqttConnectOptions.class)); + assertTrue(connectCalled.get()); + } + + @Test + public void testInboundOptionsApplied() throws Exception { + DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory(); + factory.setCleanSession(false); + factory.setConnectionTimeout(23); + factory.setKeepAliveInterval(45); + factory.setPassword("pass"); + MemoryPersistence persistence = new MemoryPersistence(); + factory.setPersistence(persistence); + final SocketFactory socketFactory = mock(SocketFactory.class); + factory.setSocketFactory(socketFactory); + final Properties props = new Properties(); + factory.setSslProperties(props); + factory.setUserName("user"); + Will will = new Will("foo", "bar".getBytes(), 2, true); + factory.setWill(will); + + factory = spy(factory); + final MqttClient client = mock(MqttClient.class); + doAnswer(new Answer() { + + @Override + public MqttClient answer(InvocationOnMock invocation) throws Throwable { + return client; + } + }).when(factory).getClientInstance(anyString(), anyString()); + + final AtomicBoolean connectCalled = new AtomicBoolean(); + doAnswer(new Answer() { + + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + MqttConnectOptions options = (MqttConnectOptions) invocation.getArguments()[0]; + assertEquals(23, options.getConnectionTimeout()); + assertEquals(45, options.getKeepAliveInterval()); + assertEquals("pass", new String(options.getPassword())); + assertSame(socketFactory, options.getSocketFactory()); + assertSame(props, options.getSSLProperties()); + assertEquals("user", options.getUserName()); + assertEquals("foo", options.getWillDestination()); + assertEquals("bar", new String(options.getWillMessage().getPayload())); + assertEquals(2, options.getWillMessage().getQos()); + connectCalled.set(true); + return null; + } + }).when(client).connect(any(MqttConnectOptions.class)); + + final AtomicReference callback = new AtomicReference(); + doAnswer(new Answer() { + + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + callback.set((MqttCallback) invocation.getArguments()[0]); + return null; + } + }).when(client).setCallback(any(MqttCallback.class)); + + when(client.isConnected()).thenReturn(true); + + MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter("foo", "bar", factory, "baz"); + QueueChannel outputChannel = new QueueChannel(); + adapter.setOutputChannel(outputChannel); + ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); + taskScheduler.initialize(); + adapter.setTaskScheduler(taskScheduler); + adapter.afterPropertiesSet(); + adapter.start(); + + verify(client, times(1)).connect(any(MqttConnectOptions.class)); + assertTrue(connectCalled.get()); + + MqttMessage message = new MqttMessage("qux".getBytes()); + callback.get().messageArrived("baz", message); + Message outMessage = outputChannel.receive(0); + assertNotNull(outMessage); + assertEquals("qux", outMessage.getPayload()); + } + +} diff --git a/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/config/xml/MqttMessageDrivenChannelAdapterParserTests-context.xml b/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/config/xml/MqttMessageDrivenChannelAdapterParserTests-context.xml new file mode 100644 index 0000000..9416250 --- /dev/null +++ b/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/config/xml/MqttMessageDrivenChannelAdapterParserTests-context.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + diff --git a/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/config/xml/MqttMessageDrivenChannelAdapterParserTests.java b/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/config/xml/MqttMessageDrivenChannelAdapterParserTests.java new file mode 100644 index 0000000..ec89563 --- /dev/null +++ b/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/config/xml/MqttMessageDrivenChannelAdapterParserTests.java @@ -0,0 +1,84 @@ +/* + * 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.mqtt.config.xml; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.integration.MessageChannel; +import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory; +import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter; +import org.springframework.integration.mqtt.support.MqttMessageConverter; +import org.springframework.integration.test.util.TestUtils; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Gary Russell + * @since 3.0 + * + */ +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class MqttMessageDrivenChannelAdapterParserTests { + + @Autowired + private MqttPahoMessageDrivenChannelAdapter oneTopicAdapter; + + @Autowired + private MqttPahoMessageDrivenChannelAdapter twoTopicsAdapter; + + @Autowired + private MessageChannel out; + + @Autowired + private MqttMessageConverter converter; + + @Autowired + private DefaultMqttPahoClientFactory clientFactory; + + @Test + public void testOneTopic() { + assertEquals("tcp://localhost:1883", TestUtils.getPropertyValue(oneTopicAdapter, "url")); + assertFalse(TestUtils.getPropertyValue(oneTopicAdapter, "autoStartup", Boolean.class)); + assertEquals(25, TestUtils.getPropertyValue(oneTopicAdapter, "phase")); + assertEquals("foo", TestUtils.getPropertyValue(oneTopicAdapter, "clientId")); + assertEquals("bar", TestUtils.getPropertyValue(oneTopicAdapter, "topic", String[].class)[0]); + assertSame(converter, TestUtils.getPropertyValue(oneTopicAdapter, "converter")); + assertEquals(123L, TestUtils.getPropertyValue(oneTopicAdapter, "messagingTemplate.sendTimeout")); + assertSame(out, TestUtils.getPropertyValue(oneTopicAdapter, "outputChannel")); + assertSame(clientFactory, TestUtils.getPropertyValue(oneTopicAdapter, "clientFactory")); + } + + @Test + public void testTwoTopics() { + assertEquals("tcp://localhost:1883", TestUtils.getPropertyValue(oneTopicAdapter, "url")); + assertFalse(TestUtils.getPropertyValue(twoTopicsAdapter, "autoStartup", Boolean.class)); + assertEquals(25, TestUtils.getPropertyValue(twoTopicsAdapter, "phase")); + assertEquals("foo", TestUtils.getPropertyValue(twoTopicsAdapter, "clientId")); + assertEquals("bar", TestUtils.getPropertyValue(twoTopicsAdapter, "topic", String[].class)[0]); + assertEquals("baz", TestUtils.getPropertyValue(twoTopicsAdapter, "topic", String[].class)[1]); + assertSame(converter, TestUtils.getPropertyValue(twoTopicsAdapter, "converter")); + assertEquals(123L, TestUtils.getPropertyValue(twoTopicsAdapter, "messagingTemplate.sendTimeout")); + assertSame(out, TestUtils.getPropertyValue(twoTopicsAdapter, "outputChannel")); + assertSame(clientFactory, TestUtils.getPropertyValue(twoTopicsAdapter, "clientFactory")); + } + +} diff --git a/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/config/xml/MqttOutboundChannelAdapterParserTests-context.xml b/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/config/xml/MqttOutboundChannelAdapterParserTests-context.xml new file mode 100644 index 0000000..203066d --- /dev/null +++ b/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/config/xml/MqttOutboundChannelAdapterParserTests-context.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + diff --git a/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/config/xml/MqttOutboundChannelAdapterParserTests.java b/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/config/xml/MqttOutboundChannelAdapterParserTests.java new file mode 100644 index 0000000..be2552f --- /dev/null +++ b/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/config/xml/MqttOutboundChannelAdapterParserTests.java @@ -0,0 +1,84 @@ +/* + * 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.mqtt.config.xml; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory; +import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler; +import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter; +import org.springframework.integration.mqtt.support.MqttMessageConverter; +import org.springframework.integration.support.converter.MessageConverter; +import org.springframework.integration.test.util.TestUtils; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Gary Russell + * @since 3.0 + * + */ +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class MqttOutboundChannelAdapterParserTests { + + @Autowired @Qualifier("withConverter.handler") + private MqttPahoMessageHandler withConverterHandler; + + @Autowired @Qualifier("withDefaultConverter.handler") + private MqttPahoMessageHandler withDefaultConverterHandler; + + @Autowired + private MqttMessageConverter converter; + + @Autowired + private DefaultMqttPahoClientFactory clientFactory; + + @Test + public void testWithConverter() { + assertEquals("tcp://localhost:1883", TestUtils.getPropertyValue(withConverterHandler, "url")); + assertFalse(TestUtils.getPropertyValue(withConverterHandler, "autoStartup", Boolean.class)); + assertEquals(25, TestUtils.getPropertyValue(withConverterHandler, "phase")); + assertEquals("foo", TestUtils.getPropertyValue(withConverterHandler, "clientId")); + assertEquals("bar", TestUtils.getPropertyValue(withConverterHandler, "defaultTopic")); + assertSame(converter, TestUtils.getPropertyValue(withConverterHandler, "converter")); + assertSame(clientFactory, TestUtils.getPropertyValue(withConverterHandler, "clientFactory")); + } + + @Test + public void testWithDefaultConverter() { + assertEquals("tcp://localhost:1883", TestUtils.getPropertyValue(withDefaultConverterHandler, "url")); + assertFalse(TestUtils.getPropertyValue(withDefaultConverterHandler, "autoStartup", Boolean.class)); + assertEquals(25, TestUtils.getPropertyValue(withDefaultConverterHandler, "phase")); + assertEquals("foo", TestUtils.getPropertyValue(withDefaultConverterHandler, "clientId")); + assertEquals("bar", TestUtils.getPropertyValue(withDefaultConverterHandler, "defaultTopic")); + assertEquals(1, TestUtils.getPropertyValue(withDefaultConverterHandler, "defaultQos")); + assertTrue(TestUtils.getPropertyValue(withDefaultConverterHandler, "defaultRetained", Boolean.class)); + MessageConverter defaultConverter = TestUtils.getPropertyValue(withDefaultConverterHandler, "converter", MessageConverter.class); + assertTrue(defaultConverter instanceof DefaultPahoMessageConverter); + assertEquals(1, TestUtils.getPropertyValue(defaultConverter, "defaultQos")); + assertTrue(TestUtils.getPropertyValue(defaultConverter, "defaultRetained", Boolean.class)); + assertSame(clientFactory, TestUtils.getPropertyValue(withDefaultConverterHandler, "clientFactory")); + } + +} diff --git a/spring-integration-mqtt/src/test/resources/log4j.properties b/spring-integration-mqtt/src/test/resources/log4j.properties new file mode 100644 index 0000000..69750fc --- /dev/null +++ b/spring-integration-mqtt/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.mqtt=INFO From ac43f8e930f119bb228e466610c91e838b9a5b83 Mon Sep 17 00:00:00 2001 From: Gunnar Hillert Date: Wed, 15 May 2013 16:44:48 -0400 Subject: [PATCH 2/2] INTEXT-64 - Polishing * Update dependency versions * Add more comments/links * Remove DocBook usage --- README.md | 4 +- spring-integration-mqtt/README.md | 5 +- spring-integration-mqtt/build.gradle | 27 +-- .../gradle/wrapper/gradle-wrapper.jar | Bin 39770 -> 45502 bytes .../gradle/wrapper/gradle-wrapper.properties | 4 +- spring-integration-mqtt/gradlew | 2 +- spring-integration-mqtt/gradlew.bat | 180 +++++++++--------- .../core/DefaultMqttPahoClientFactory.java | 6 + ...stractMqttMessageDrivenChannelAdapter.java | 2 +- .../xml/spring-integration-mqttadapter.gif | Bin 572 -> 539 bytes .../docbook/SIAdapterLowerPrefix.xml | 73 ------- .../src/reference/docbook/history.xml | 8 - .../src/reference/docbook/images/logo.png | Bin 17393 -> 0 bytes .../src/reference/docbook/index.xml | 68 ------- .../src/reference/docbook/resources.xml | 17 -- .../src/reference/docbook/whats-new.xml | 11 -- .../mqtt/BackTobackAdapterTests.java | 2 +- .../integration/mqtt/BrokerRunning.java | 2 +- .../integration/mqtt/MqttAdapterTests.java | 2 +- ...essageDrivenChannelAdapterParserTests.java | 2 +- ...MqttOutboundChannelAdapterParserTests.java | 2 +- 21 files changed, 118 insertions(+), 299 deletions(-) delete mode 100644 spring-integration-mqtt/src/reference/docbook/SIAdapterLowerPrefix.xml delete mode 100644 spring-integration-mqtt/src/reference/docbook/history.xml delete mode 100644 spring-integration-mqtt/src/reference/docbook/images/logo.png delete mode 100644 spring-integration-mqtt/src/reference/docbook/index.xml delete mode 100644 spring-integration-mqtt/src/reference/docbook/resources.xml delete mode 100644 spring-integration-mqtt/src/reference/docbook/whats-new.xml diff --git a/README.md b/README.md index 8a367c4..bc1a562 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ The Spring Integration Extensions project provides extension modules for [Spring * [XQuery][] Support * [Splunk][] Support * [Amazon Web Services (AWS)][] Support +* [MQ Telemetry Transport (MQTT)][] Support ## Samples @@ -133,4 +134,5 @@ The Spring Integration Extensions Framework is released under version 2.0 of the [Websockets]: http://www.html5rocks.com/en/tutorials/websockets/basics/ [XQuery]: http://en.wikipedia.org/wiki/XQuery [Splunk]:http://www.splunk.com/ -[Amazon Web Services (AWS)]: http://aws.amazon.com/ \ No newline at end of file +[Amazon Web Services (AWS)]: http://aws.amazon.com/ +[MQ Telemetry Transport (MQTT)]: http://mqtt.org/ \ No newline at end of file diff --git a/spring-integration-mqtt/README.md b/spring-integration-mqtt/README.md index 1a49cf9..986bfea 100644 --- a/spring-integration-mqtt/README.md +++ b/spring-integration-mqtt/README.md @@ -1,7 +1,7 @@ Spring Integration Mqtt Adapters ================================================= -`inbound` and `outbound` channel adapters are provided for Mqtt. The current implementation uses the [Eclipse Paho][] client. +`inbound` and `outbound` channel adapters are provided for [MQ Telemetry Transport (MQTT)][]. The current implementation uses the [Eclipse Paho][] client. Example configurations... @@ -73,4 +73,5 @@ For more information, please also don't forget to visit the [Spring Integration] [Spring Integration Pattern Catalog]: https://github.com/SpringSource/spring-integration-pattern-catalog [Stack Overflow]: http://stackoverflow.com/faq [Eclipse Paho]: http://www.eclipse.org/paho/ -[open paho bug]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=382471 \ No newline at end of file +[open paho bug]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=382471 +[MQ Telemetry Transport (MQTT)]: http://mqtt.org/ \ No newline at end of file diff --git a/spring-integration-mqtt/build.gradle b/spring-integration-mqtt/build.gradle index 7e53dda..313d539 100644 --- a/spring-integration-mqtt/build.gradle +++ b/spring-integration-mqtt/build.gradle @@ -1,12 +1,9 @@ -description = 'Spring Integration Mqtt Adapter' +description = 'Spring Integration MQTT 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' @@ -26,9 +23,9 @@ sourceCompatibility=1.6 targetCompatibility=1.6 ext { - junitVersion = '4.10' - log4jVersion = '1.2.12' - mockitoVersion = '1.9.0' + junitVersion = '4.11' + log4jVersion = '1.2.17' + mockitoVersion = '1.9.5' springVersion = '3.1.3.RELEASE' springIntegrationVersion = '3.0.0.BUILD-SNAPSHOT' @@ -63,7 +60,7 @@ dependencies { 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" + jacoco group: "org.jacoco", name: "org.jacoco.agent", version: "0.6.2.201302030002", classifier: "runtime" } @@ -92,12 +89,6 @@ artifacts { archives javadocJar } -apply plugin: 'docbook-reference' - -reference { - sourceDir = file('src/reference/docbook') -} - apply plugin: 'sonar' sonar { @@ -174,7 +165,7 @@ task schemaZip(type: Zip) { task docsZip(type: Zip) { group = 'Distribution' classifier = 'docs' - description = "Builds -${classifier} archive containing api and reference " + + description = "Builds -${classifier} archive containing the api " + "for deployment at static.springframework.org/spring-integration/docs." from('src/dist') { @@ -184,10 +175,6 @@ task docsZip(type: Zip) { from (api) { into 'api' } - - from (reference) { - into 'reference' - } } task distZip(type: Zip, dependsOn: [docsZip, schemaZip]) { @@ -262,5 +249,5 @@ task dist(dependsOn: assemble) { task wrapper(type: Wrapper) { description = 'Generates gradlew[.bat] scripts' - gradleVersion = '1.3' + gradleVersion = '1.6' } diff --git a/spring-integration-mqtt/gradle/wrapper/gradle-wrapper.jar b/spring-integration-mqtt/gradle/wrapper/gradle-wrapper.jar index 81dcde627d5cfbc12c96dc5113aac79b66a15118..7f1e239c8466c730b569575c494a89e6bb4c416a 100644 GIT binary patch delta 40482 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 HzmEJ5WPKjZgJG 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 jp=e>}33O6j diff --git a/spring-integration-mqtt/gradle/wrapper/gradle-wrapper.properties b/spring-integration-mqtt/gradle/wrapper/gradle-wrapper.properties index 66b16f7..6281daf 100644 --- a/spring-integration-mqtt/gradle/wrapper/gradle-wrapper.properties +++ b/spring-integration-mqtt/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Sep 05 13:34:36 EDT 2012 +#Wed May 15 15:57:24 EDT 2013 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 +distributionUrl=http\://services.gradle.org/distributions/gradle-1.6-bin.zip diff --git a/spring-integration-mqtt/gradlew b/spring-integration-mqtt/gradlew index cf12650..e61422d 100755 --- a/spring-integration-mqtt/gradlew +++ b/spring-integration-mqtt/gradlew @@ -101,7 +101,7 @@ if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; 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" + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi diff --git a/spring-integration-mqtt/gradlew.bat b/spring-integration-mqtt/gradlew.bat index 8a0b282..aec9973 100644 --- a/spring-integration-mqtt/gradlew.bat +++ b/spring-integration-mqtt/gradlew.bat @@ -1,90 +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 +@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-mqtt/src/main/java/org/springframework/integration/mqtt/core/DefaultMqttPahoClientFactory.java b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/core/DefaultMqttPahoClientFactory.java index 1eda5d8..2445bb4 100644 --- a/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/core/DefaultMqttPahoClientFactory.java +++ b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/core/DefaultMqttPahoClientFactory.java @@ -27,6 +27,7 @@ import org.eclipse.paho.client.mqttv3.MqttException; /** * Creates a default {@link MqttClient} and a set of options as configured. * @author Gary Russell + * @author Gunnar Hillert * @since 1.0 * */ @@ -78,6 +79,11 @@ public class DefaultMqttPahoClientFactory implements MqttPahoClientFactory { this.userName = userName; } + /** + * Will be used to set the "Last Will and Testament" (LWT) for the connection. + * + * @see MqttConnectOptions + */ public void setWill(Will will) { this.will = will; } diff --git a/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/inbound/AbstractMqttMessageDrivenChannelAdapter.java b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/inbound/AbstractMqttMessageDrivenChannelAdapter.java index 0806980..5e21c72 100644 --- a/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/inbound/AbstractMqttMessageDrivenChannelAdapter.java +++ b/spring-integration-mqtt/src/main/java/org/springframework/integration/mqtt/inbound/AbstractMqttMessageDrivenChannelAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * 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. diff --git a/spring-integration-mqtt/src/main/resources/org/springframework/integration/mqtt/config/xml/spring-integration-mqttadapter.gif b/spring-integration-mqtt/src/main/resources/org/springframework/integration/mqtt/config/xml/spring-integration-mqttadapter.gif index 41b369fece0e576ecd920400b221fd33abbaf5c4..210e0764fa4c1e5baebcdea156bc3f3e1f97a2c9 100644 GIT binary patch delta 223 zcmdnPGMh!l-P6s&GEsm*fZ-Vf2>k#5zhmmUxhwYUK74UvakX5Ir{2{`v3t9c*EPl; zZwV<4FrV96vUzsb(T31G2ldI!j9rrh7;S-AuwL;e3nK%A7lRJS9FU0&>=g}OP0cN> zZS5UiVp7tgolUwb+!B^bo#NbDF7g`I3LTQl!ZL;i`c@|Gvd)?!YVNi++U-0ZMrJ~G zt`5c>ays@-f{qq?or>z7ZUW|}{YulyTioH(#+4t%gM#T&cV;c S$;-#zeDnV2hy5Lm4AuZLRYElY delta 337 zcmV-X0j~a=1iS=)M@dFFIbje05CG-?00000|Ns9kO_eoPm57zD8BM31sJy$w(NA!l zP#PA_Qat j7a - - MqttAdapter Adapter - - The Spring Integration MqttAdapter Adapter provides... - - - - Outbound Channel adapter - - - Outbound Gateway - - - Inbound Channel Adapter - - - -
- Java Implementation - Each of the provided components will use the - org.springframework.integration.mqtt.core.MqttAdapterExecutor - class... - -
-
- Common Configuration Attributes - - Certain configuration parameters are shared amongst all MqttAdapter - components and are described below: - - - auto-startup - - Lifecycle attribute signaling if this component should - be started during Application Context startup. - Defaults to true. - Optional. - - - id - - Identifies the underlying Spring bean definition, which - is an instance of either EventDrivenConsumer - or PollingConsumer. - Optional. - - -
- -
- Outbound Channel Adapter - - The MqttAdapter Outbound channel adapter allows you to... - -
-
- Outbound Gateway - - Outbound gateways are similar to outbound channel adapters except that it can also be used to - get a result on the reply channel after performing - the given... - -
-
- Inbound Channel Adapter - - An inbound channel adapter is used to execute... - -
- -
diff --git a/spring-integration-mqtt/src/reference/docbook/history.xml b/spring-integration-mqtt/src/reference/docbook/history.xml deleted file mode 100644 index 549be21..0000000 --- a/spring-integration-mqtt/src/reference/docbook/history.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - Change History - - - diff --git a/spring-integration-mqtt/src/reference/docbook/images/logo.png b/spring-integration-mqtt/src/reference/docbook/images/logo.png deleted file mode 100644 index 8c71c13a0e5606e9d87f75e37fe2874cb44beb51..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17393 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 - - - Spring Integration MqttAdapter Adapter - MqttAdapter Adapter ${version} - Spring Integration - ${version} - - - - - - - - - - - - - - Gary Russell - - - © SpringSource Inc., 2012 - - - - - - - What's new? - - - For those who are already familiar with Spring Integration, this - chapter - provides a brief overview of the new features of version 2.2. If you are - interested in the changes and features, that were introduced in - earlier - versions, please take a look at chapter: - - - - - - - - - - Integration Adapters - - This section covers the various Channel Adapters and Messaging - Gateways provided - by Spring Integration to support Message-based communication with - external systems. - - - - - - Appendices - - Advanced Topics and Additional Resources - - - - diff --git a/spring-integration-mqtt/src/reference/docbook/resources.xml b/spring-integration-mqtt/src/reference/docbook/resources.xml deleted file mode 100644 index 109faa9..0000000 --- a/spring-integration-mqtt/src/reference/docbook/resources.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - 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. - -
- -
diff --git a/spring-integration-mqtt/src/reference/docbook/whats-new.xml b/spring-integration-mqtt/src/reference/docbook/whats-new.xml deleted file mode 100644 index f037937..0000000 --- a/spring-integration-mqtt/src/reference/docbook/whats-new.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - What's new? - - This chapter provides an overview of the new features and improvements - that have been added to the MqttAdapter Adapter: - - - diff --git a/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/BackTobackAdapterTests.java b/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/BackTobackAdapterTests.java index 60e44f3..66e2ba3 100644 --- a/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/BackTobackAdapterTests.java +++ b/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/BackTobackAdapterTests.java @@ -31,7 +31,7 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; /** * @author Gary Russell - * @since 3.0 + * @since 1.0 * */ public class BackTobackAdapterTests { diff --git a/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/BrokerRunning.java b/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/BrokerRunning.java index f7c5726..57587d8 100644 --- a/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/BrokerRunning.java +++ b/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/BrokerRunning.java @@ -32,7 +32,7 @@ import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory; /** * @author Gary Russell - * @since 3.0 + * @since 1.0 * */ public class BrokerRunning extends TestWatcher { diff --git a/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/MqttAdapterTests.java b/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/MqttAdapterTests.java index bdce33f..92c44c4 100644 --- a/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/MqttAdapterTests.java +++ b/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/MqttAdapterTests.java @@ -53,7 +53,7 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; /** * @author Gary Russell - * @since 3.0 + * @since 1.0 * */ public class MqttAdapterTests { diff --git a/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/config/xml/MqttMessageDrivenChannelAdapterParserTests.java b/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/config/xml/MqttMessageDrivenChannelAdapterParserTests.java index ec89563..5b3452f 100644 --- a/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/config/xml/MqttMessageDrivenChannelAdapterParserTests.java +++ b/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/config/xml/MqttMessageDrivenChannelAdapterParserTests.java @@ -32,7 +32,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * @author Gary Russell - * @since 3.0 + * @since 1.0 * */ @ContextConfiguration diff --git a/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/config/xml/MqttOutboundChannelAdapterParserTests.java b/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/config/xml/MqttOutboundChannelAdapterParserTests.java index be2552f..71a752b 100644 --- a/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/config/xml/MqttOutboundChannelAdapterParserTests.java +++ b/spring-integration-mqtt/src/test/java/org/springframework/integration/mqtt/config/xml/MqttOutboundChannelAdapterParserTests.java @@ -35,7 +35,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * @author Gary Russell - * @since 3.0 + * @since 1.0 * */ @ContextConfiguration