diff --git a/pom.xml b/pom.xml index f09d62eb..f2db4f00 100755 --- a/pom.xml +++ b/pom.xml @@ -71,6 +71,7 @@ spring-cloud-task-samples spring-cloud-task-docs spring-cloud-task-batch + spring-cloud-task-stream diff --git a/spring-cloud-task-docs/src/main/asciidoc/features.adoc b/spring-cloud-task-docs/src/main/asciidoc/features.adoc index cf5780d8..918115da 100644 --- a/spring-cloud-task-docs/src/main/asciidoc/features.adoc +++ b/spring-cloud-task-docs/src/main/asciidoc/features.adoc @@ -209,3 +209,53 @@ marking the final state of the task. } ``` +=== Launching a task from a Spring Cloud Stream + +Allows a user to launch tasks from a stream. This is done by creating a sink that +listens for a message that contains a `TaskLaunchRequest` as its payload. The +TaskLaunchRequest contains the maven coordinates to the Task that is to be executed. It +also has a Map that contains the environment variables that will be used by the Task. + +NOTE: If the payload is of a different type then the sink will throw an exception. + +For example a stream can be created that has a processor that takes in data from a +http source and creates a `GenericMessage` that contains the `TaskLaunchRequest` and sends +the message to its output channel. The task sink would then receive the message from its +input channnel and then launch the task. + +To create a taskSink a user needs to only create a spring boot app that includes the +following annotation `EnableTaskLauncher`. The code would look something like this: + +``` +@SpringBootApplication +@EnableTaskLauncher +public class TaskSinkApplication { + public static void main(String[] args) { + SpringApplication.run(TaskSinkApplication.class, args); + } +} +``` + +A sample Sink and Processor have been made available to you in the samples module +of the Spring Cloud Task project. To install these samples into your local maven +repository execute a maven build from the `spring-cloud-task-samples` directory with the +property `skipInstall` set to false. For example: +`mvn clean install -DskipInstall=false`. + + +==== Spring Cloud Data Flow + +To create a stream in Spring Cloud Data Flow first we would want to register the Task Sink +Application we created. In the example below we are registering the Processor and Sink +sample applications using the Spring Cloud Data Flow shell: + +``` +module register --name taskSink --type sink --coordinates io.spring:tasksink:1.0.0.BUILD-SNAPSHOT +module register --name taskProcessor --type processor --coordinates io.spring:taskprocessor:1.0.0.BUILD-SNAPSHOT +``` + +To create a stream from the Spring Cloud Data Flow shell would look like this: + +``` +stream create foo --definition "http --server.port=9000|taskProcessor|taskSink" --deploy +``` diff --git a/spring-cloud-task-samples/pom.xml b/spring-cloud-task-samples/pom.xml index 542eb8a8..20bda936 100644 --- a/spring-cloud-task-samples/pom.xml +++ b/spring-cloud-task-samples/pom.xml @@ -24,6 +24,8 @@ timestamp batch-job + tasksink + taskprocessor diff --git a/spring-cloud-task-samples/taskprocessor/.mvn/wrapper/maven-wrapper.jar b/spring-cloud-task-samples/taskprocessor/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 00000000..5fd4d502 Binary files /dev/null and b/spring-cloud-task-samples/taskprocessor/.mvn/wrapper/maven-wrapper.jar differ diff --git a/spring-cloud-task-samples/taskprocessor/.mvn/wrapper/maven-wrapper.properties b/spring-cloud-task-samples/taskprocessor/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000..eb919476 --- /dev/null +++ b/spring-cloud-task-samples/taskprocessor/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.3/apache-maven-3.3.3-bin.zip \ No newline at end of file diff --git a/spring-cloud-task-samples/taskprocessor/README.adoc b/spring-cloud-task-samples/taskprocessor/README.adoc new file mode 100644 index 00000000..b6535359 --- /dev/null +++ b/spring-cloud-task-samples/taskprocessor/README.adoc @@ -0,0 +1,42 @@ += TaskProcessor + +Is a stream processor that will accept messages with a string payload and create a +`TaskLaunchRequest` that it will send to the next app in the stream. The payload of the +message that is sent will be added to the properties of the `TaskLaunchRequest` under +the `payload` key. + +By default the processor will add the timestamp-task sample coordinates if no task is +specified. + +NOTE: if using defaults make sure to install the timestamp-task into your local maven repo +by executing the following build in the timestamp-task module: + +[source,shell,indent=2] +---- +$ ./mvnw clean install -DskipInstall=false +---- + +== The parameters offered by the TaskProcessor are as follows: +* *group* establishes the group for the task maven coordinates. Default is `io.spring`. +* *artifact* establishes the artifact for the task maven coordinates. Default is `timestamp-task`. +* *classifiers* establishes the classifier for the task maven coordinates. Default is null. +* *extension* establishes the extension for the task maven coordinates. Default is jar. +* *data-source-url* sets the spring_datasource_url for the task. Default is null. +* *data-source-driver-class-name* establishes the spring_datasource_driverClassName for the task. Default is null. +* *data-source-user-name* establishes the spring_datasource_username for the task. Default is null. +* *data-source-password* establishes the spring_datasource_password for the task. Default is null. + +== Requirements: + +* Java 7 or Above + +== Build: + +[source,shell,indent=2] +---- +$ ./mvnw clean install -DskipInstall=false +---- + +== Dependencies: + +The task processor requires an instance of Redis to be running. diff --git a/spring-cloud-task-samples/taskprocessor/mvnw b/spring-cloud-task-samples/taskprocessor/mvnw new file mode 100755 index 00000000..a1ba1bf5 --- /dev/null +++ b/spring-cloud-task-samples/taskprocessor/mvnw @@ -0,0 +1,233 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # + # Look for the Apple JDKs first to preserve the existing behaviour, and then look + # for the new JDKs provided by Oracle. + # + if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then + # + # Apple JDKs + # + export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then + # + # Apple JDKs + # + export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then + # + # Oracle JDKs + # + export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then + # + # Apple JDKs + # + export JAVA_HOME=`/usr/libexec/java_home` + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Migwn, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" + # TODO classpath? +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + 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 + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` +fi + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + local basedir=$(pwd) + local wdir=$(pwd) + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + wdir=$(cd "$wdir/.."; pwd) + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)} +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} "$@" diff --git a/spring-cloud-task-samples/taskprocessor/mvnw.cmd b/spring-cloud-task-samples/taskprocessor/mvnw.cmd new file mode 100644 index 00000000..2b934e89 --- /dev/null +++ b/spring-cloud-task-samples/taskprocessor/mvnw.cmd @@ -0,0 +1,145 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +set MAVEN_CMD_LINE_ARGS=%* + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" + +set WRAPPER_JAR="".\.mvn\wrapper\maven-wrapper.jar"" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS% +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% \ No newline at end of file diff --git a/spring-cloud-task-samples/taskprocessor/pom.xml b/spring-cloud-task-samples/taskprocessor/pom.xml new file mode 100644 index 00000000..9d0838a2 --- /dev/null +++ b/spring-cloud-task-samples/taskprocessor/pom.xml @@ -0,0 +1,121 @@ + + + 4.0.0 + + io.spring + taskprocessor + 1.0.0.BUILD-SNAPSHOT + jar + + TaskProcessor + Task processor sample application + + + org.springframework.boot + spring-boot-starter-parent + 1.3.3.RELEASE + + + + + UTF-8 + true + + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.cloud + spring-cloud-task-stream + 1.0.0.BUILD-SNAPSHOT + + + org.springframework.cloud + spring-cloud-stream + 1.0.0.BUILD-SNAPSHOT + + + org.springframework.cloud + spring-cloud-stream-binder-redis + 1.0.0.BUILD-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-redis + + + org.springframework.boot + spring-boot-configuration-processor + + + + org.springframework.cloud + spring-cloud-stream-test-support + 1.0.0.BUILD-SNAPSHOT + test + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.3 + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar + + + + + + + + + + maven-deploy-plugin + + true + + + + maven-install-plugin + + ${skipInstall} + + + + + + + + diff --git a/spring-cloud-task-samples/taskprocessor/src/main/java/io/spring/TaskProcessor.java b/spring-cloud-task-samples/taskprocessor/src/main/java/io/spring/TaskProcessor.java new file mode 100644 index 00000000..c9f03e98 --- /dev/null +++ b/spring-cloud-task-samples/taskprocessor/src/main/java/io/spring/TaskProcessor.java @@ -0,0 +1,68 @@ +/* + * Copyright 2016 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 io.spring; + +import java.util.HashMap; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.stream.annotation.EnableBinding; +import org.springframework.cloud.stream.messaging.Processor; +import org.springframework.cloud.task.launcher.TaskLaunchRequest; +import org.springframework.integration.annotation.Transformer; +import org.springframework.messaging.support.GenericMessage; +import org.springframework.util.StringUtils; + +/** + * A processor that takes the maven repository coordinates and datasource configuration + * for a task and sends a {@link TaskLaunchRequest} message to a task sink. + * + * @author Glenn Renfro + */ +@EnableBinding(Processor.class) +@EnableConfigurationProperties(TaskProcessorProperties.class) +public class TaskProcessor { + + @Autowired + private TaskProcessorProperties processorProperties; + + @Transformer(inputChannel = Processor.INPUT, outputChannel = Processor.OUTPUT) + public Object setupRequest(String message) { + Map properties = new HashMap(); + if(StringUtils.hasText(processorProperties.getDataSourceUrl())){ + properties.put("spring_datasource_url",processorProperties.getDataSourceUrl()); + } + if(StringUtils.hasText(processorProperties.getDataSourceDriverClassName())){ + properties.put("spring_datasource_driverClassName",processorProperties.getDataSourceDriverClassName()); + } + if(StringUtils.hasText(processorProperties.getDataSourceUserName())){ + properties.put("spring_datasource_username",processorProperties.getDataSourceUserName()); + } + if(StringUtils.hasText(processorProperties.getDataSourcePassword())){ + properties.put("spring_datasource_password",processorProperties.getDataSourcePassword()); + } + properties.put("payload", message); + + TaskLaunchRequest request = new TaskLaunchRequest(processorProperties.getArtifact(), + processorProperties.getGroup(), processorProperties.getVersion(), processorProperties.getExtension(), + processorProperties.getClassifiers(), properties); + + return new GenericMessage(request); + } + +} diff --git a/spring-cloud-task-samples/taskprocessor/src/main/java/io/spring/TaskProcessorApplication.java b/spring-cloud-task-samples/taskprocessor/src/main/java/io/spring/TaskProcessorApplication.java new file mode 100644 index 00000000..58fbb8ef --- /dev/null +++ b/spring-cloud-task-samples/taskprocessor/src/main/java/io/spring/TaskProcessorApplication.java @@ -0,0 +1,31 @@ +/* + * Copyright 2016 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 io.spring; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author Glenn Renfro + */ +@SpringBootApplication +public class TaskProcessorApplication { + + public static void main(String[] args) { + SpringApplication.run(TaskProcessorApplication.class, args); + } +} diff --git a/spring-cloud-task-samples/taskprocessor/src/main/java/io/spring/TaskProcessorProperties.java b/spring-cloud-task-samples/taskprocessor/src/main/java/io/spring/TaskProcessorProperties.java new file mode 100644 index 00000000..1606648e --- /dev/null +++ b/spring-cloud-task-samples/taskprocessor/src/main/java/io/spring/TaskProcessorProperties.java @@ -0,0 +1,126 @@ +/* + * Copyright 2016 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 io.spring; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * @author Glenn Renfro + */ +@ConfigurationProperties +public class TaskProcessorProperties { + + private static final String DEFAULT_GROUP = "io.spring"; + + private static final String DEFAULT_ARTIFACT = "timestamp-task"; + + private static final String DEFAULT_VERSION = "1.0.0.BUILD-SNAPSHOT"; + + private static final String DEFAULT_EXTENSION = "jar"; + + + private String group = DEFAULT_GROUP; + + private String artifact = DEFAULT_ARTIFACT; + + private String version = DEFAULT_VERSION; + + private String extension = DEFAULT_EXTENSION; + + private String classifiers; + + private String dataSourceUrl; + + private String dataSourceDriverClassName; + + private String dataSourceUserName; + + private String dataSourcePassword; + + + public String getExtension() { + return extension; + } + + public void setExtension(String extension) { + this.extension = extension; + } + + public String getClassifiers() { + return classifiers; + } + + public void setClassifiers(String classifiers) { + this.classifiers = classifiers; + } + + public String getDataSourceUrl() { + return dataSourceUrl; + } + + public void setDataSourceUrl(String dataSourceUrl) { + this.dataSourceUrl = dataSourceUrl; + } + + public String getDataSourceDriverClassName() { + return dataSourceDriverClassName; + } + + public void setDataSourceDriverClassName(String dataSourceDriverClassName) { + this.dataSourceDriverClassName = dataSourceDriverClassName; + } + + public String getDataSourceUserName() { + return dataSourceUserName; + } + + public void setDataSourceUserName(String dataSourceUserName) { + this.dataSourceUserName = dataSourceUserName; + } + + public String getDataSourcePassword() { + return dataSourcePassword; + } + + public void setDataSourcePassword(String dataSourcePassword) { + this.dataSourcePassword = dataSourcePassword; + } + + public void setGroup(String group) { + this.group = group; + } + + public String getGroup() { + return group; + } + + public String getArtifact() { + return artifact; + } + + public void setArtifact(String artifact) { + this.artifact = artifact; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } +} diff --git a/spring-cloud-task-samples/taskprocessor/src/test/java/io/spring/TaskProcessorApplicationTests.java b/spring-cloud-task-samples/taskprocessor/src/test/java/io/spring/TaskProcessorApplicationTests.java new file mode 100644 index 00000000..623432ae --- /dev/null +++ b/spring-cloud-task-samples/taskprocessor/src/test/java/io/spring/TaskProcessorApplicationTests.java @@ -0,0 +1,63 @@ +/* + * Copyright 2016 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 io.spring; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.springframework.cloud.stream.test.matcher.MessageQueueMatcher.receivesPayloadThat; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.cloud.stream.annotation.Bindings; +import org.springframework.cloud.stream.messaging.Processor; +import org.springframework.cloud.stream.test.binder.MessageCollector; +import org.springframework.cloud.task.launcher.TaskLaunchRequest; +import org.springframework.messaging.support.GenericMessage; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Glenn Renfro + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = TaskProcessorApplication.class) +public class TaskProcessorApplicationTests { + + private static final String DEFAULT_PAYLOAD = "hello"; + + @Autowired + @Bindings(TaskProcessor.class) + protected Processor channels; + + @Autowired + protected MessageCollector collector; + + @Test + public void test() throws InterruptedException{ + channels.input().send(new GenericMessage(DEFAULT_PAYLOAD)); + Map properties = new HashMap(); + properties.put("payload", DEFAULT_PAYLOAD); + TaskLaunchRequest expectedRequest = new TaskLaunchRequest("timestamp-task", + "io.spring", "1.0.0.BUILD-SNAPSHOT", "jar", null, properties); + assertThat(collector.forChannel(channels.output()), receivesPayloadThat(is(expectedRequest))); + } + +} diff --git a/spring-cloud-task-samples/tasksink/.mvn/wrapper/maven-wrapper.jar b/spring-cloud-task-samples/tasksink/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 00000000..5fd4d502 Binary files /dev/null and b/spring-cloud-task-samples/tasksink/.mvn/wrapper/maven-wrapper.jar differ diff --git a/spring-cloud-task-samples/tasksink/.mvn/wrapper/maven-wrapper.properties b/spring-cloud-task-samples/tasksink/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000..eb919476 --- /dev/null +++ b/spring-cloud-task-samples/tasksink/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.3/apache-maven-3.3.3-bin.zip \ No newline at end of file diff --git a/spring-cloud-task-samples/tasksink/README.adoc b/spring-cloud-task-samples/tasksink/README.adoc new file mode 100644 index 00000000..5c1a5193 --- /dev/null +++ b/spring-cloud-task-samples/tasksink/README.adoc @@ -0,0 +1,19 @@ += TaskSink + +Is a stream sink that will accept messages with the payload of `TaskLaunchRequest` and +launch the task that was specified in the request. + +== Requirements: + +* Java 7 or Above + +== Build: + +[source,shell,indent=2] +---- +$ ./mvnw clean install -DskipInstall=false +---- + +== Dependencies: + +The task processor requires an instance of Redis to be running. diff --git a/spring-cloud-task-samples/tasksink/mvnw b/spring-cloud-task-samples/tasksink/mvnw new file mode 100755 index 00000000..a1ba1bf5 --- /dev/null +++ b/spring-cloud-task-samples/tasksink/mvnw @@ -0,0 +1,233 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # + # Look for the Apple JDKs first to preserve the existing behaviour, and then look + # for the new JDKs provided by Oracle. + # + if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then + # + # Apple JDKs + # + export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then + # + # Apple JDKs + # + export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then + # + # Oracle JDKs + # + export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then + # + # Apple JDKs + # + export JAVA_HOME=`/usr/libexec/java_home` + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Migwn, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" + # TODO classpath? +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + 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 + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` +fi + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + local basedir=$(pwd) + local wdir=$(pwd) + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + wdir=$(cd "$wdir/.."; pwd) + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)} +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} "$@" diff --git a/spring-cloud-task-samples/tasksink/mvnw.cmd b/spring-cloud-task-samples/tasksink/mvnw.cmd new file mode 100644 index 00000000..2b934e89 --- /dev/null +++ b/spring-cloud-task-samples/tasksink/mvnw.cmd @@ -0,0 +1,145 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +set MAVEN_CMD_LINE_ARGS=%* + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" + +set WRAPPER_JAR="".\.mvn\wrapper\maven-wrapper.jar"" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS% +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% \ No newline at end of file diff --git a/spring-cloud-task-samples/tasksink/pom.xml b/spring-cloud-task-samples/tasksink/pom.xml new file mode 100644 index 00000000..b01bab5a --- /dev/null +++ b/spring-cloud-task-samples/tasksink/pom.xml @@ -0,0 +1,116 @@ + + + 4.0.0 + + io.spring + tasksink + 1.0.0.BUILD-SNAPSHOT + jar + + TaskSink + Task sink sample project + + + org.springframework.boot + spring-boot-starter-parent + 1.3.3.RELEASE + + + + + UTF-8 + true + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.cloud + spring-cloud-deployer-local + 1.0.0.BUILD-SNAPSHOT + + + org.springframework.cloud + spring-cloud-task-stream + 1.0.0.BUILD-SNAPSHOT + + + org.springframework.cloud + spring-cloud-stream-binder-redis + 1.0.0.BUILD-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-redis + + + + org.springframework.cloud + spring-cloud-stream-test-support + 1.0.0.BUILD-SNAPSHOT + test + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.3 + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar + + + + + + + + + + maven-deploy-plugin + + true + + + + maven-install-plugin + + ${skipInstall} + + + + + + + diff --git a/spring-cloud-task-samples/tasksink/src/main/java/io/spring/TaskSinkApplication.java b/spring-cloud-task-samples/tasksink/src/main/java/io/spring/TaskSinkApplication.java new file mode 100644 index 00000000..0bf0e77a --- /dev/null +++ b/spring-cloud-task-samples/tasksink/src/main/java/io/spring/TaskSinkApplication.java @@ -0,0 +1,35 @@ +/* + * Copyright 2016 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 io.spring; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.task.launcher.annotation.EnableTaskLauncher; + +/** + * A task sink sample application for launching tasks. + * + * @author Glenn Renfro + */ +@SpringBootApplication +@EnableTaskLauncher +public class TaskSinkApplication { + + public static void main(String[] args) { + SpringApplication.run(TaskSinkApplication.class, args); + } +} diff --git a/spring-cloud-task-samples/tasksink/src/test/java/io/spring/TaskSinkApplicationTests.java b/spring-cloud-task-samples/tasksink/src/test/java/io/spring/TaskSinkApplicationTests.java new file mode 100644 index 00000000..b2703408 --- /dev/null +++ b/spring-cloud-task-samples/tasksink/src/test/java/io/spring/TaskSinkApplicationTests.java @@ -0,0 +1,69 @@ +/* + * Copyright 2016 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 io.spring; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.util.HashMap; +import java.util.Map; + +import io.spring.configuration.TaskSinkConfiguration; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.cloud.deployer.spi.task.LaunchState; +import org.springframework.cloud.stream.annotation.Bindings; +import org.springframework.cloud.stream.messaging.Sink; +import org.springframework.cloud.task.launcher.TaskLaunchRequest; +import org.springframework.cloud.task.launcher.TaskLauncherSink; +import org.springframework.context.ApplicationContext; +import org.springframework.messaging.support.GenericMessage; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Glenn Renfro + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = TaskSinkApplication.class) +public class TaskSinkApplicationTests { + + @Autowired + ApplicationContext context; + + @Autowired + @Bindings(TaskLauncherSink.class) + private Sink sink; + + @Test + public void testLaunch() { + assertNotNull(this.sink.input()); + + TaskSinkConfiguration.TestTaskLauncher testTaskLauncher = + (TaskSinkConfiguration.TestTaskLauncher) context.getBean(TaskSinkConfiguration.TestTaskLauncher.class); + + Map properties = new HashMap(); + properties.put("server.port", "0"); + TaskLaunchRequest request = new TaskLaunchRequest("timestamp-task", + "org.springframework.cloud.task.module","1.0.0.BUILD-SNAPSHOT", "jar", + "exec", properties); + GenericMessage message = new GenericMessage(request); + this.sink.input().send(message); + assertEquals(LaunchState.complete, testTaskLauncher.status("TESTSTATUS").getState()); + } +} diff --git a/spring-cloud-task-samples/tasksink/src/test/java/io/spring/configuration/TaskSinkConfiguration.java b/spring-cloud-task-samples/tasksink/src/test/java/io/spring/configuration/TaskSinkConfiguration.java new file mode 100644 index 00000000..2defb583 --- /dev/null +++ b/spring-cloud-task-samples/tasksink/src/test/java/io/spring/configuration/TaskSinkConfiguration.java @@ -0,0 +1,62 @@ +/* + * Copyright 2016 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 io.spring.configuration; + +import org.springframework.cloud.deployer.spi.core.AppDeploymentRequest; +import org.springframework.cloud.deployer.spi.task.LaunchState; +import org.springframework.cloud.deployer.spi.task.TaskLauncher; +import org.springframework.cloud.deployer.spi.task.TaskStatus; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author Glenn Renfro + */ +@Configuration +public class TaskSinkConfiguration { + + @Bean + public TaskLauncher taskLauncher() { + return new TestTaskLauncher(); + } + + public class TestTaskLauncher implements TaskLauncher { + + public static final String LAUNCH_ID = "TEST_LAUNCH_ID"; + + private LaunchState state = LaunchState.unknown; + + @Override + public String launch(AppDeploymentRequest request) { + state = LaunchState.complete; + return null; + } + + @Override + public void cancel(String id) { + + } + + @Override + public TaskStatus status(String id) { + String taskLaunchId = LAUNCH_ID; + TaskStatus taskStatus = new TaskStatus(taskLaunchId, state, null); + return taskStatus; + } + } + +} diff --git a/spring-cloud-task-samples/timestamp/pom.xml b/spring-cloud-task-samples/timestamp/pom.xml index 37972b42..5deb9d7a 100644 --- a/spring-cloud-task-samples/timestamp/pom.xml +++ b/spring-cloud-task-samples/timestamp/pom.xml @@ -5,7 +5,7 @@ 4.0.0 - org.springframework.cloud + io.spring timestamp-task jar Timestamp Task @@ -20,6 +20,7 @@ org.springframework.cloud.task.timestamp.TaskApplication + true @@ -96,7 +97,7 @@ maven-install-plugin - true + ${skipInstall} diff --git a/spring-cloud-task-stream/.mvn/wrapper/maven-wrapper.jar b/spring-cloud-task-stream/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 00000000..5fd4d502 Binary files /dev/null and b/spring-cloud-task-stream/.mvn/wrapper/maven-wrapper.jar differ diff --git a/spring-cloud-task-stream/.mvn/wrapper/maven-wrapper.properties b/spring-cloud-task-stream/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000..eb919476 --- /dev/null +++ b/spring-cloud-task-stream/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.3/apache-maven-3.3.3-bin.zip \ No newline at end of file diff --git a/spring-cloud-task-stream/pom.xml b/spring-cloud-task-stream/pom.xml new file mode 100644 index 00000000..a04e8f5a --- /dev/null +++ b/spring-cloud-task-stream/pom.xml @@ -0,0 +1,66 @@ + + + 4.0.0 + + spring-cloud-task-stream + jar + Spring Cloud Task Stream + Allows Tasks to be a part of a stream + + + org.springframework.cloud + spring-cloud-task-parent + 1.0.0.BUILD-SNAPSHOT + + + + + org.springframework.cloud + spring-cloud-stream + 1.0.0.BUILD-SNAPSHOT + + + org.springframework.cloud + spring-cloud-deployer-local + 1.0.0.BUILD-SNAPSHOT + true + + + org.springframework.boot + spring-boot-starter-redis + + + org.springframework.integration + spring-integration-test + 4.2.2.RELEASE + test + + + org.springframework.cloud + spring-cloud-task-core + test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter + test + + + org.springframework.cloud + spring-cloud-stream-test-support + 1.0.0.BUILD-SNAPSHOT + test + + + org.springframework.cloud + spring-cloud-stream-binder-redis + 1.0.0.BUILD-SNAPSHOT + + + diff --git a/spring-cloud-task-stream/src/main/java/org/springframework/cloud/task/launcher/TaskLaunchRequest.java b/spring-cloud-task-stream/src/main/java/org/springframework/cloud/task/launcher/TaskLaunchRequest.java new file mode 100644 index 00000000..d2afeffb --- /dev/null +++ b/spring-cloud-task-stream/src/main/java/org/springframework/cloud/task/launcher/TaskLaunchRequest.java @@ -0,0 +1,167 @@ +/* + * Copyright 2016 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.cloud.task.launcher; + + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * Request that contains the maven repository and property information required by the + * TaskLauncherSink to launch the task. + * + * @author Glenn Renfro + */ +public class TaskLaunchRequest implements Serializable{ + + private static final long serialVersionUID = 1L; + private String artifact; + private String taskGroupId; + private String taskVersion; + private String taskExtension; + private String taskClassifier; + private Map properties; + + /** + * Constructor for the TaskLaunchRequest; + * @param artifact is maven artifact coordinate for the task. Must not be empty nor null. + * @param taskGroupId is maven groupId coordinate for the task. Must not be empty nor null. + * @param taskVersion is maven version coordinate for the task. Must not be empty nor null. + * @param taskExtension is maven extension coordinate for the task. + * @param taskClassifier is maven classifier coordinate for the task. + * @param properties is the environment variables for this task. + */ + public TaskLaunchRequest(String artifact, String taskGroupId, String taskVersion, + String taskExtension, String taskClassifier, + Map properties) { + Assert.hasText(artifact, "artifact must not be empty nor null."); + Assert.hasText(taskGroupId, "taskGroupID must not be empty nor null."); + Assert.hasText(taskVersion, "taskVersion must not be empty nor null."); + Assert.hasText(taskExtension, "taskExtension must not be empty nor null."); + + this.artifact = artifact; + this.taskGroupId = taskGroupId; + this.taskVersion = taskVersion; + this.taskExtension = taskExtension; + this.taskClassifier = taskClassifier; + this.properties = properties == null ? new HashMap() : properties; + } + + /** + * Retrieves the group maven coordinate for the task. + * @return group maven coordinate for the task. + */ + public String getTaskGroupId() { + return taskGroupId; + } + + /** + * Retrieves the version maven coordinate for the task. + * @return version maven coordinate for the task. + */ + public String getTaskVersion() { + return taskVersion; + } + + /** + * Retrieves the extension maven coordinate for the task. + * @return extension maven coordinate for the task. + */ + public String getTaskExtension() { + return taskExtension; + } + + /** + * Retrieves the classifier maven coordinate for the task. + * @return classifier maven coordinate for the task. + */ + public String getTaskClassifier() { + return taskClassifier; + } + + /** + * Retrieves the artifact maven coordinate for the task. + * @return artifact maven coordinate for the task. + */ + public String getArtifact() { + return artifact; + } + + /** + * Retrieves the environment variables for the task. + * @return map containing the environment variables for the task. + */ + + public Map getProperties() { + return properties; + } + + @Override + public String toString() { + String coordinates = taskGroupId + ":" + artifact + ":" + taskVersion ; + if(StringUtils.hasText(taskClassifier)){ + coordinates = coordinates + ":" + taskClassifier; + } + coordinates = coordinates + ":" + taskExtension; + return coordinates; + } + + @Override + public boolean equals(Object o) { + if (this == o){ + return true; + } + if (o == null || getClass() != o.getClass()){ + return false; + } + + TaskLaunchRequest that = (TaskLaunchRequest) o; + + if (!artifact.equals(that.artifact)){ + return false; + } + if (!taskGroupId.equals(that.taskGroupId)){ + return false; + } + if (!taskVersion.equals(that.taskVersion)){ + return false; + } + if (!taskExtension.equals(that.taskExtension)){ + return false; + } + if (taskClassifier != null ? !taskClassifier.equals(that.taskClassifier) : that.taskClassifier != null){ + return false; + } + return properties != null ? properties.equals(that.properties) : that.properties == null; + + } + + @Override + public int hashCode() { + int result = artifact != null ? artifact.hashCode() : 0; + result = 31 * result + (taskGroupId != null ? taskGroupId.hashCode() : 0); + result = 31 * result + (taskVersion != null ? taskVersion.hashCode() : 0); + result = 31 * result + (taskExtension != null ? taskExtension.hashCode() : 0); + result = 31 * result + (taskClassifier != null ? taskClassifier.hashCode() : 0); + result = 31 * result + (properties != null ? properties.hashCode() : 0); + return result; + } +} diff --git a/spring-cloud-task-stream/src/main/java/org/springframework/cloud/task/launcher/TaskLauncherConfiguration.java b/spring-cloud-task-stream/src/main/java/org/springframework/cloud/task/launcher/TaskLauncherConfiguration.java new file mode 100644 index 00000000..df96aa33 --- /dev/null +++ b/spring-cloud-task-stream/src/main/java/org/springframework/cloud/task/launcher/TaskLauncherConfiguration.java @@ -0,0 +1,47 @@ +/* + * Copyright 2016 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.cloud.task.launcher; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.cloud.deployer.spi.local.LocalDeployerProperties; +import org.springframework.cloud.deployer.spi.local.LocalTaskLauncher; +import org.springframework.cloud.deployer.spi.task.TaskLauncher; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Creates the appropriate Task Launcher Configuration based on the TaskLauncher + * that is available in the classpath. + * @author Glenn Renfro + */ + +@Configuration +@ConditionalOnClass({TaskLauncher.class}) +public class TaskLauncherConfiguration { + + @Configuration + @ConditionalOnMissingBean(name = "taskLauncher") + @ConditionalOnClass({LocalTaskLauncher.class}) + protected static class LocalTaskDeployerConfiguration { + @Bean + public TaskLauncher taskLauncher() { + return new LocalTaskLauncher(new LocalDeployerProperties()); + } + } + +} diff --git a/spring-cloud-task-stream/src/main/java/org/springframework/cloud/task/launcher/TaskLauncherSink.java b/spring-cloud-task-stream/src/main/java/org/springframework/cloud/task/launcher/TaskLauncherSink.java new file mode 100644 index 00000000..2c0f1f74 --- /dev/null +++ b/spring-cloud-task-stream/src/main/java/org/springframework/cloud/task/launcher/TaskLauncherSink.java @@ -0,0 +1,70 @@ +/* + * Copyright 2016 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.cloud.task.launcher; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.deployer.resource.maven.MavenResource; +import org.springframework.cloud.deployer.spi.core.AppDefinition; +import org.springframework.cloud.deployer.spi.core.AppDeploymentRequest; +import org.springframework.cloud.deployer.spi.task.TaskLauncher; +import org.springframework.cloud.stream.annotation.EnableBinding; +import org.springframework.cloud.stream.messaging.Sink; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.util.Assert; + +/** + * A sink stream application that launches a tasks. + * + * @author Glenn Renfro + */ + +@EnableBinding(Sink.class) +public class TaskLauncherSink { + + private final static Logger logger = LoggerFactory.getLogger(TaskLauncherSink.class); + + @Autowired + public TaskLauncher taskLauncher; + + /** + * Launches a task upon the receipt of a valid TaskLaunchRequest. + * @param request is a TaskLaunchRequest containing the information required to launch + * a task. + */ + @ServiceActivator(inputChannel = Sink.INPUT) + public void taskLauncherSink(TaskLaunchRequest request) { + launchTask(request); + } + + private void launchTask(TaskLaunchRequest taskLaunchRequest) { + Assert.notNull(taskLauncher, "TaskLauncher has not been initialized"); + logger.info("Launching Task for the following resource " + taskLaunchRequest); + MavenResource resource = new MavenResource.Builder() + .artifactId(taskLaunchRequest.getArtifact()) + .groupId(taskLaunchRequest.getTaskGroupId()) + .version(taskLaunchRequest.getTaskVersion()) + .extension(taskLaunchRequest.getTaskExtension()) + .classifier(taskLaunchRequest.getTaskClassifier()) + .build(); + AppDefinition definition = new AppDefinition(taskLaunchRequest.getArtifact(), taskLaunchRequest.getProperties()); + AppDeploymentRequest request = new AppDeploymentRequest(definition, resource); + taskLauncher.launch(request); + } + +} diff --git a/spring-cloud-task-stream/src/main/java/org/springframework/cloud/task/launcher/annotation/EnableTaskLauncher.java b/spring-cloud-task-stream/src/main/java/org/springframework/cloud/task/launcher/annotation/EnableTaskLauncher.java new file mode 100644 index 00000000..441fb635 --- /dev/null +++ b/spring-cloud-task-stream/src/main/java/org/springframework/cloud/task/launcher/annotation/EnableTaskLauncher.java @@ -0,0 +1,61 @@ +/* + * Copyright 2016 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.cloud.task.launcher.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.cloud.deployer.spi.task.TaskLauncher; +import org.springframework.cloud.task.launcher.TaskLaunchRequest; +import org.springframework.cloud.task.launcher.TaskLauncherConfiguration; +import org.springframework.cloud.task.launcher.TaskLauncherSink; +import org.springframework.context.annotation.Import; + +/** + *

+ * Enable this boot app to be a sink to receive a {@link TaskLaunchRequest} and use the + * {@link TaskLauncher} to launch the task. + *

+ * + *
+ * @Configuration
+ * @EnableTaskLauncher
+ * public class AppConfig {
+ *
+ * 	@Bean
+ * 	public MyCommandLineRunner myCommandLineRunner() {
+ * 		return new MyCommandLineRunner()
+ * 	}
+ * }
+ * 
+ * + * Note that only one of your configuration classes needs to have the @EnableTaskLauncher + * annotation. + * + * @author Glenn Renfro + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@Import({TaskLauncherConfiguration.class, TaskLauncherSink.class}) +public @interface EnableTaskLauncher { +} diff --git a/spring-cloud-task-stream/src/test/java/org/springframework/cloud/task/launcher/TaskLauncherSinkTests.java b/spring-cloud-task-stream/src/test/java/org/springframework/cloud/task/launcher/TaskLauncherSinkTests.java new file mode 100644 index 00000000..357111b2 --- /dev/null +++ b/spring-cloud-task-stream/src/test/java/org/springframework/cloud/task/launcher/TaskLauncherSinkTests.java @@ -0,0 +1,83 @@ +/* + * Copyright 2016 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.cloud.task.launcher; + +import static org.junit.Assert.assertEquals; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.cloud.deployer.spi.task.LaunchState; +import org.springframework.cloud.stream.annotation.Bindings; +import org.springframework.cloud.stream.messaging.Sink; +import org.springframework.cloud.task.launcher.configuration.TaskConfiguration; +import org.springframework.cloud.task.launcher.util.TaskLauncherSinkApplication; +import org.springframework.context.ApplicationContext; +import org.springframework.messaging.support.GenericMessage; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = {TaskLauncherSinkApplication.class, TaskConfiguration.class} ) +public class TaskLauncherSinkTests { + + private final static String DEFAULT_STATUS = "test_status"; + + @Autowired + private ApplicationContext context; + + @Autowired + @Bindings(TaskLauncherSink.class) + private Sink sink; + + + @Test + public void testSuccess() { + TaskConfiguration.TestTaskLauncher testTaskLauncher = + context.getBean(TaskConfiguration.TestTaskLauncher.class); + + Map properties = new HashMap<>(); + properties.put("server.port", "0"); + TaskLaunchRequest request = new TaskLaunchRequest("timestamp-task", + "org.springframework.cloud.task.module","1.0.0.BUILD-SNAPSHOT", "jar", + "exec", properties); + GenericMessage message = new GenericMessage<>(request); + this.sink.input().send(message); + assertEquals(LaunchState.complete, testTaskLauncher.status(DEFAULT_STATUS).getState()); + } + + @Test + public void testNoRun() { + TaskConfiguration.TestTaskLauncher testTaskLauncher = + context.getBean(TaskConfiguration.TestTaskLauncher.class); + + assertEquals(LaunchState.unknown, testTaskLauncher.status(DEFAULT_STATUS).getState()); + } + + @Test(expected = IllegalArgumentException.class) + public void testNoTaskLauncher() { + Map properties = new HashMap<>(); + properties.put("server.port", "0"); + TaskLauncherSink sink = new TaskLauncherSink(); + sink.taskLauncherSink(new TaskLaunchRequest("timestamp-task", + "org.springframework.cloud.task.module","1.0.0.BUILD-SNAPSHOT", "jar", + "exec", properties)); + } +} diff --git a/spring-cloud-task-stream/src/test/java/org/springframework/cloud/task/launcher/configuration/TaskConfiguration.java b/spring-cloud-task-stream/src/test/java/org/springframework/cloud/task/launcher/configuration/TaskConfiguration.java new file mode 100644 index 00000000..00848056 --- /dev/null +++ b/spring-cloud-task-stream/src/test/java/org/springframework/cloud/task/launcher/configuration/TaskConfiguration.java @@ -0,0 +1,60 @@ +/* + * Copyright 2016 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.cloud.task.launcher.configuration; + +import org.springframework.cloud.deployer.spi.core.AppDeploymentRequest; +import org.springframework.cloud.deployer.spi.task.LaunchState; +import org.springframework.cloud.deployer.spi.task.TaskLauncher; +import org.springframework.cloud.deployer.spi.task.TaskStatus; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author Glenn Renfro + */ + +@Configuration +public class TaskConfiguration { + + @Bean + public TaskLauncher taskLauncher(){ + return new TestTaskLauncher(); + } + + public static class TestTaskLauncher implements TaskLauncher{ + + public static final String LAUNCH_ID = "TEST_LAUNCH_ID"; + + private LaunchState state = LaunchState.unknown; + + @Override + public String launch(AppDeploymentRequest request) { + state = LaunchState.complete; + return null; + } + + @Override + public void cancel(String id) { + + } + + @Override + public TaskStatus status(String id) { + return new TaskStatus(LAUNCH_ID, state, null); + } + } +} diff --git a/spring-cloud-task-stream/src/test/java/org/springframework/cloud/task/launcher/util/TaskLauncherSinkApplication.java b/spring-cloud-task-stream/src/test/java/org/springframework/cloud/task/launcher/util/TaskLauncherSinkApplication.java new file mode 100644 index 00000000..141d7eb3 --- /dev/null +++ b/spring-cloud-task-stream/src/test/java/org/springframework/cloud/task/launcher/util/TaskLauncherSinkApplication.java @@ -0,0 +1,33 @@ +/* + * Copyright 2016 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.cloud.task.launcher.util; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.task.launcher.annotation.EnableTaskLauncher; + +/** + * @author Glenn Renfro + */ +@SpringBootApplication +@EnableTaskLauncher +public class TaskLauncherSinkApplication { + + public static void main(String[] args) { + SpringApplication.run(TaskLauncherSinkApplication.class, args); + } +}