diff --git a/README.adoc b/README.adoc
index 98e3fa145e..cb8f560811 100644
--- a/README.adoc
+++ b/README.adoc
@@ -1547,11 +1547,15 @@ IMPORTANT: You need to have all the necessary Groovy plugins
Intellij IDEA having both Eclipse Groovy Compiler Plugin & GMavenPlus Intellij Plugin
results in properly imported project.
+IMPORTANT: Spring Cloud Contract builds Docker images. Remember to
+have Docker installed.
+
=== Project structure
Here you can find the Spring Cloud Contract folder structure
```
+├── docker
├── samples
├── scripts
├── spring-cloud-contract-dependencies
@@ -1564,6 +1568,7 @@ Here you can find the Spring Cloud Contract folder structure
└── tests
```
+ - `docker` - folder contains docker images
- `samples` - folder contains test samples together with standalone ones used also to build documentation
- `scripts` - contains scripts to build and test `Spring Cloud Contract` with Maven, Gradle and standalone projects
- `spring-cloud-contract-dependencies` - contains Spring Cloud Contract BOM
diff --git a/docker/pom.xml b/docker/pom.xml
new file mode 100644
index 0000000000..96bb5842bb
--- /dev/null
+++ b/docker/pom.xml
@@ -0,0 +1,40 @@
+
+
+ 4.0.0
+
+
+ org.springframework.cloud
+ spring-cloud-contract-parent
+ 1.2.3.BUILD-SNAPSHOT
+ ..
+
+
+ spring-cloud-contract-docker-parent
+ pom
+ 1.2.3.BUILD-SNAPSHOT
+ Spring Cloud Contract Docker Parent
+ Spring Cloud Contract Docker Parent
+
+
+ spring-cloud
+
+
+
+ spring-cloud-contract-docker
+ spring-cloud-contract-stub-runner-docker
+
+
+
+
+
+ maven-deploy-plugin
+ 2.8.2
+
+ true
+
+
+
+
+
+
diff --git a/docker/spring-cloud-contract-docker/Dockerfile b/docker/spring-cloud-contract-docker/Dockerfile
new file mode 100644
index 0000000000..3935e3f246
--- /dev/null
+++ b/docker/spring-cloud-contract-docker/Dockerfile
@@ -0,0 +1,12 @@
+FROM anapsix/alpine-java:8_jdk
+
+MAINTAINER Marcin Grzejszczak
+
+COPY project /spring-cloud-contract/
+WORKDIR /spring-cloud-contract/
+# Let's copy the downloaded deps to .m2 and the gradle cache
+COPY target/maven_dependencies /root/.m2/repository/
+COPY target/gradle_dependencies /root/.gradle/
+# Let's download gradle wrapper if for some reason it hasn't been downloaded
+RUN ./gradlew clean build --stacktrace -x copyOutput || echo "Expected to fail"
+CMD ["./build.sh"]
diff --git a/docker/spring-cloud-contract-docker/get_dependencies.sh b/docker/spring-cloud-contract-docker/get_dependencies.sh
new file mode 100755
index 0000000000..520e770abb
--- /dev/null
+++ b/docker/spring-cloud-contract-docker/get_dependencies.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+set -e
+
+WRAPPER_VERSION="4.4.1"
+GRADLE_BIN_DIR="gradle-${WRAPPER_VERSION}-bin"
+GRADLE_WRAPPER_DIR="${HOME}/.gradle/wrapper/dists/${GRADLE_BIN_DIR}"
+CURRENT_DIR="$( pwd )"
+GRADLE_OUTPUT_DIR="${CURRENT_DIR}/target/gradle_dependencies/"
+pushd project
+rm -rf .gradle
+./gradlew wrapper --gradle-version "${WRAPPER_VERSION}"
+./gradlew clean build -g "${GRADLE_OUTPUT_DIR}" -x copyOutput || echo "Expected to fail the build"
+if [ -d "${GRADLE_WRAPPER_DIR}" ]; then
+ echo "Copying Gradle Wrapper version [${WRAPPER_VERSION}]"
+ mkdir -p "${GRADLE_OUTPUT_DIR}/wrapper/dists/"
+ cp -r "${GRADLE_WRAPPER_DIR}" "${GRADLE_OUTPUT_DIR}/wrapper/dists/"
+else
+ echo "Gradle Wrapper [${GRADLE_WRAPPER_DIR}] not found. Will not copy it"
+fi
+rm -rf build
+popd
diff --git a/docker/spring-cloud-contract-docker/pom.xml b/docker/spring-cloud-contract-docker/pom.xml
new file mode 100644
index 0000000000..138b3e2307
--- /dev/null
+++ b/docker/spring-cloud-contract-docker/pom.xml
@@ -0,0 +1,115 @@
+
+
+ 4.0.0
+
+
+ org.springframework.cloud
+ spring-cloud-contract-docker-parent
+ 1.2.3.BUILD-SNAPSHOT
+ ..
+
+
+ spring-cloud-contract-docker
+ pom
+ 1.2.3.BUILD-SNAPSHOT
+ Spring Cloud Contract Docker
+ Spring Cloud Contract Docker
+
+
+
+ org.codehaus.groovy
+ groovy-all
+ compile
+
+
+ org.codehaus.groovy
+ groovy-ant
+ compile
+
+
+ org.codehaus.groovy
+ groovy-groovydoc
+ compile
+
+
+ org.springframework.cloud
+ spring-cloud-starter-contract-verifier
+ compile
+
+
+ org.springframework.cloud
+ spring-cloud-contract-gradle-plugin
+ runtime
+
+
+ io.spring.gradle
+ dependency-management-plugin
+ 1.0.4.RELEASE
+ runtime
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+ 3.0.0
+
+
+ copy-dependencies
+ package
+
+ copy-dependencies
+
+
+ ${project.build.directory}/maven_dependencies
+ false
+ true
+ true
+ true
+
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 1.6.0
+
+
+ gradle
+ package
+
+ ./get_dependencies.sh
+
+
+ exec
+
+
+
+
+
+ com.spotify
+ dockerfile-maven-plugin
+ 1.3.7
+
+
+ default
+
+ build
+ push
+
+
+
+
+ ${docker.registry.organization}/spring-cloud-contract
+ ${project.version}
+ ${env.DOCKER_HUB_USERNAME}
+ ${env.DOCKER_HUB_PASSWORD}
+
+
+
+
+
diff --git a/docker/spring-cloud-contract-docker/project/build.gradle b/docker/spring-cloud-contract-docker/project/build.gradle
new file mode 100644
index 0000000000..e653879a8d
--- /dev/null
+++ b/docker/spring-cloud-contract-docker/project/build.gradle
@@ -0,0 +1,114 @@
+buildscript {
+ repositories {
+ mavenLocal()
+ mavenCentral()
+ if (!"${verifierVersion}".contains("RELEASE")) {
+ maven { url "http://repo.spring.io/snapshot" }
+ maven { url "http://repo.spring.io/milestone" }
+ maven { url "http://repo.spring.io/release" }
+ }
+ }
+
+ dependencies {
+ classpath "io.spring.gradle:dependency-management-plugin:1.0.4.RELEASE"
+ classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:${verifierVersion}"
+ }
+}
+
+group = getProp("PROJECT_GROUP") ?: 'com.example'
+version = getProp("PROJECT_VERSION") ?: '0.0.1-SNAPSHOT'
+
+repositories {
+ mavenLocal()
+ mavenCentral()
+ if (!"${verifierVersion}".contains("RELEASE")) {
+ maven { url "http://repo.spring.io/snapshot" }
+ maven { url "http://repo.spring.io/milestone" }
+ maven { url "http://repo.spring.io/release" }
+ }
+}
+
+apply plugin: 'groovy'
+apply plugin: 'io.spring.dependency-management'
+apply plugin: 'spring-cloud-contract'
+apply plugin: 'maven-publish'
+
+dependencyManagement {
+ imports {
+ mavenBom "org.springframework.cloud:spring-cloud-contract-dependencies:${verifierVersion}"
+ }
+}
+
+dependencies {
+ testCompile("org.springframework.cloud:spring-cloud-starter-contract-verifier")
+}
+
+test {
+ testLogging {
+ exceptionFormat = 'full'
+ afterSuite { desc, result ->
+ if (!desc.parent) {
+ println "Results: (${result.testCount} tests, ${result.successfulTestCount} successes, ${result.failedTestCount} failures, ${result.skippedTestCount} skipped)"
+ if (result.testCount == 0) {
+ throw new IllegalStateException("No tests were found. Failing the build")
+ }
+ }
+ }
+ }
+}
+
+// tests - contracts
+contracts {
+ baseClassForTests = "contracts.RestBase"
+ testMode = "EXPLICIT"
+ contractsDslDir = new File("/contracts")
+}
+
+task wrapper(type: Wrapper) {
+ gradleVersion = '4.4.1'
+}
+
+task cleanOutput(type: Delete) {
+ def dirName = "/spring-cloud-contract-output"
+ file(dirName).list().each {
+ f -> delete "${dirName}/${f}"
+ }
+}
+
+task copyOutput(type: Copy) {
+ dependsOn("cleanOutput")
+ from 'build'
+ into '/spring-cloud-contract-output'
+}
+
+test.finalizedBy("copyOutput")
+
+publishing {
+ repositories {
+ maven {
+ url getProp('REPO_WITH_BINARIES_URL') ?: 'http://localhost:8081/artifactory/libs-release-local'
+ credentials {
+ username getProp('REPO_WITH_BINARIES_USERNAME') ?: 'admin'
+ password getProp('REPO_WITH_BINARIES_PASSWORD') ?: 'password'
+ }
+ }
+ }
+ publications {
+ mavenJava(MavenPublication) {
+ from components.java
+ }
+ }
+}
+
+// explicitly disable artifacts publication
+String publishArtifacts = getProp("PUBLISH_ARTIFACTS") ?: "true"
+boolean publishEnabled = Boolean.parseBoolean(publishArtifacts)
+publish.setEnabled(publishEnabled)
+
+project.tasks.findAll { it.name.startsWith('publish') }*.setEnabled(publishEnabled)
+
+String getProp(String propName) {
+ return hasProperty(propName) ?
+ (getProperty(propName) ?: System.properties[propName]) : System.properties[propName] ?:
+ System.getenv(propName)
+}
diff --git a/docker/spring-cloud-contract-docker/project/build.sh b/docker/spring-cloud-contract-docker/project/build.sh
new file mode 100755
index 0000000000..48c3809a1c
--- /dev/null
+++ b/docker/spring-cloud-contract-docker/project/build.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+export PROJECT_NAME="${PROJECT_NAME:-example}"
+echo "Setting project name to [${PROJECT_NAME}]"
+echo "rootProject.name='${PROJECT_NAME}'" >> settings.gradle
+echo "Running the build"
+./gradlew clean build publish --stacktrace
diff --git a/docker/spring-cloud-contract-docker/project/gradle.properties b/docker/spring-cloud-contract-docker/project/gradle.properties
new file mode 100644
index 0000000000..0307a47d8b
--- /dev/null
+++ b/docker/spring-cloud-contract-docker/project/gradle.properties
@@ -0,0 +1,3 @@
+org.gradle.daemon=false
+verifierVersion=1.2.3.BUILD-SNAPSHOT
+bootVersion=1.5.9.RELEASE
diff --git a/docker/spring-cloud-contract-docker/project/gradle/wrapper/gradle-wrapper.jar b/docker/spring-cloud-contract-docker/project/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000000..99340b4ad1
Binary files /dev/null and b/docker/spring-cloud-contract-docker/project/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/docker/spring-cloud-contract-docker/project/gradle/wrapper/gradle-wrapper.properties b/docker/spring-cloud-contract-docker/project/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000000..2c2bbe5f9a
--- /dev/null
+++ b/docker/spring-cloud-contract-docker/project/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.4.1-bin.zip
diff --git a/docker/spring-cloud-contract-docker/project/gradlew b/docker/spring-cloud-contract-docker/project/gradlew
new file mode 100755
index 0000000000..cccdd3d517
--- /dev/null
+++ b/docker/spring-cloud-contract-docker/project/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+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" -a "$nonstop" = "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 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"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # 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
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/docker/spring-cloud-contract-docker/project/gradlew.bat b/docker/spring-cloud-contract-docker/project/gradlew.bat
new file mode 100644
index 0000000000..e95643d6a2
--- /dev/null
+++ b/docker/spring-cloud-contract-docker/project/gradlew.bat
@@ -0,0 +1,84 @@
+@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
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@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=
+
+@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 Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_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=%*
+
+: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/docker/spring-cloud-contract-docker/project/src/test/java/contracts/RestBase.java b/docker/spring-cloud-contract-docker/project/src/test/java/contracts/RestBase.java
new file mode 100644
index 0000000000..24e7b06354
--- /dev/null
+++ b/docker/spring-cloud-contract-docker/project/src/test/java/contracts/RestBase.java
@@ -0,0 +1,41 @@
+package contracts;
+
+import io.restassured.RestAssured;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.runner.RunWith;
+import org.junit.validator.ValidateWith;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.util.StringUtils;
+
+/**
+ * @author Marcin Grzejszczak
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = RestBase.Config.class,
+ webEnvironment = SpringBootTest.WebEnvironment.NONE)
+public abstract class RestBase {
+
+ @Value("${APPLICATION_BASE_URL}") String url;
+ @Value("${APPLICATION_USERNAME:}") String username;
+ @Value("${APPLICATION_PASSWORD:}") String password;
+
+ @Before
+ public void setup() {
+ RestAssured.baseURI = this.url;
+ if (StringUtils.hasText(this.username)) {
+ RestAssured.authentication =
+ RestAssured.basic(this.username, this.password);
+ }
+ }
+
+ @Configuration
+ @EnableAutoConfiguration
+ protected static class Config {
+
+ }
+}
diff --git a/docker/spring-cloud-contract-stub-runner-docker/Dockerfile b/docker/spring-cloud-contract-stub-runner-docker/Dockerfile
new file mode 100644
index 0000000000..607ecd5cff
--- /dev/null
+++ b/docker/spring-cloud-contract-stub-runner-docker/Dockerfile
@@ -0,0 +1,9 @@
+FROM anapsix/alpine-java:8_jdk
+
+MAINTAINER Marcin Grzejszczak
+
+ENV SERVER_PORT 8083
+
+VOLUME /tmp
+ADD target/libs/stub-runner-boot.jar stub-runner-boot.jar
+ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/stub-runner-boot.jar"]
diff --git a/docker/spring-cloud-contract-stub-runner-docker/pom.xml b/docker/spring-cloud-contract-stub-runner-docker/pom.xml
new file mode 100644
index 0000000000..a714d3217c
--- /dev/null
+++ b/docker/spring-cloud-contract-stub-runner-docker/pom.xml
@@ -0,0 +1,78 @@
+
+
+ 4.0.0
+
+
+ org.springframework.cloud
+ spring-cloud-contract-docker-parent
+ 1.2.3.BUILD-SNAPSHOT
+ ..
+
+
+ spring-cloud-contract-stub-runner-docker
+ pom
+ 1.2.3.BUILD-SNAPSHOT
+ Spring Cloud Contract Stub Runner Docker
+ Spring Cloud Contract Stub Runner Docker
+
+
+
+ org.springframework.cloud
+ spring-cloud-contract-stub-runner-boot
+ compile
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+ 3.0.0
+
+
+ copy-dependencies
+ package
+
+ copy
+
+
+
+
+ ${project.groupId}
+ spring-cloud-contract-stub-runner-boot
+ ${project.version}
+ jar
+ true
+ ${project.build.directory}/libs
+ stub-runner-boot.jar
+
+
+
+
+
+
+
+ com.spotify
+ dockerfile-maven-plugin
+ 1.3.7
+
+
+ default
+
+ build
+ push
+
+
+
+
+ ${docker.registry.organization}/spring-cloud-contract-stub-runner
+ ${project.version}
+ ${env.DOCKER_HUB_USERNAME}
+ ${env.DOCKER_HUB_PASSWORD}
+
+
+
+
+
diff --git a/docs/src/main/asciidoc/building.adoc b/docs/src/main/asciidoc/building.adoc
index 7ddc05175d..0812d93248 100644
--- a/docs/src/main/asciidoc/building.adoc
+++ b/docs/src/main/asciidoc/building.adoc
@@ -5,11 +5,15 @@ IMPORTANT: You need to have all the necessary Groovy plugins
Intellij IDEA having both Eclipse Groovy Compiler Plugin & GMavenPlus Intellij Plugin
results in properly imported project.
+IMPORTANT: Spring Cloud Contract builds Docker images. Remember to
+have Docker installed.
+
=== Project structure
Here you can find the Spring Cloud Contract folder structure
```
+├── docker
├── samples
├── scripts
├── spring-cloud-contract-dependencies
@@ -22,6 +26,7 @@ Here you can find the Spring Cloud Contract folder structure
└── tests
```
+ - `docker` - folder contains docker images
- `samples` - folder contains test samples together with standalone ones used also to build documentation
- `scripts` - contains scripts to build and test `Spring Cloud Contract` with Maven, Gradle and standalone projects
- `spring-cloud-contract-dependencies` - contains Spring Cloud Contract BOM
diff --git a/docs/src/main/asciidoc/verifier_setup.adoc b/docs/src/main/asciidoc/verifier_setup.adoc
index baed9d65c8..a72afcc4e8 100644
--- a/docs/src/main/asciidoc/verifier_setup.adoc
+++ b/docs/src/main/asciidoc/verifier_setup.adoc
@@ -1,9 +1,10 @@
== Spring Cloud Contract Verifier Setup
-You can set up Spring Cloud Contract Verifier in either of two ways
+You can set up Spring Cloud Contract Verifier in the following ways:
* <>
* <>
+* <>
[[gradle-project]]
=== Gradle Project
@@ -898,3 +899,162 @@ More details about WireMock scenarios can be found at
http://wiremock.org/stateful-behaviour.html[http://wiremock.org/stateful-behaviour.html]
Spring Cloud Contract Verifier also generates tests with a guaranteed order of execution.
+
+[[docker-project]]
+=== Docker Project
+
+We're publishing a `spring-cloud/spring-cloud-contract` Docker image
+that contains a project that will generate tests and execute them in `EXPLICIT` mode
+against a running application.
+
+TIP: The `EXPLICIT` mode means that the tests generated from contracts will send
+real requests and not the mocked ones.
+
+==== Short intro to Maven, JARs and Binary storage
+
+Since the Docker image can be used by non JVM projects, it's good to
+explain the basic terms behind Spring Cloud Contract packaging defaults.
+
+Part of the following definitions were taken from the https://maven.apache.org/glossary.html[Maven Glossary]
+
+- `Project`: Maven thinks in terms of projects. Everything that you
+will build are projects. Those projects follow a well defined
+“Project Object Model”. Projects can depend on other projects,
+in which case the latter are called “dependencies”. A project may
+consistent of several subprojects, however these subprojects are still
+treated equally as projects.
+- `Artifact`: An artifact is something that is either produced or used
+by a project. Examples of artifacts produced by Maven for a project
+include: JARs, source and binary distributions. Each artifact
+is uniquely identified by a group id and an artifact ID which is
+unique within a group.
+- `JAR`: JAR stands for Java ARchive. It's a format based on
+the ZIP file format. Spring Cloud Contract packages the contracts and generated
+stubs in a JAR file.
+- `GroupId`: A group ID is a universally unique identifier for a project.
+While this is often just the project name (eg. commons-collections),
+it is helpful to use a fully-qualified package name to distinguish it
+from other projects with a similar name (eg. org.apache.maven).
+Typically, when published to the Artifact Manager, the `GroupId` will get
+slash separated and form part of the URL. E.g. for group id `com.example`
+and artifact id `application` would be `/com/example/application/`.
+- `Classifier`: The Maven dependency notation looks as follows:
+`groupId:artifactId:version:classifier`. The classifier is additional suffix
+passed to the dependency. E.g. `stubs`, `sources`. The same dependency
+e.g. `com.example:application` can produce multiple artifacts that
+differ from each other with the classifier.
+- `Artifact manager`: When you generate binaries / sources / packages, you would
+like them to be available for others to download / reference or reuse. In case
+of the JVM world those artifacts would be JARs, for Ruby these are gems
+and for Docker those would be Docker images. You can store those artifacts
+in a manager. Examples of such managers can be https://jfrog.com/artifactory/[Artifactory]
+or http://www.sonatype.org/nexus/[Nexus].
+
+==== How it works
+
+The image searches for contracts under the `/contracts` folder.
+The output from running the tests will be available under
+`/spring-cloud-contract/build` folder (it's useful for debugging
+purposes).
+
+It's enough for you to mount your contracts, pass the environment variables
+ and the image will:
+
+- generate the contract tests
+- execute the tests against the provided URL
+- generate the http://wiremock.org[WireMock] stubs
+- (optional - turned on by default) publish the stubs to a Artifact Manager
+
+===== Environment Variables
+
+The Docker image requires some environment variables to point to
+your running application, to the Artifact manager instance etc.
+
+- `PROJECT_GROUP` - your project's group id. Defaults to `com.example`.
+- `PROJECT_VERSION` - your project's version. Defaults to `0.0.1-SNAPSHOT`
+- `PROJECT_NAME` - artifact id. Defaults to `example`
+- `REPO_WITH_BINARIES_URL` - URL of your Artifact Manager. Defaults to `http://localhost:8081/artifactory/libs-release-local`
+which is the default URL of https://jfrog.com/artifactory/[Artifactory] running locally
+- `REPO_WITH_BINARIES_USERNAME` - (optional) username when the Artifact Manager is secured
+- `REPO_WITH_BINARIES_PASSWORD` - (optional) password when the Artifact Manager is secured
+- `PUBLISH_ARTIFACTS` - if set to `true` then will publish artifact to binary storage. Defaults to `true`.
+
+These environment variables are used when tests are executed:
+
+- `APPLICATION_BASE_URL` - url against which tests should be executed.
+Remember that it has to be accessible from the Docker container (e.g. `localhost`
+will not work)
+- `APPLICATION_USERNAME` - (optional) username for basic authentication to your application
+- `APPLICATION_PASSWORD` - (optional) password for basic authentication to your application
+
+==== Example of usage
+
+Let's take a look at a simple MVC application
+
+```bash
+$ git clone https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs
+$ cd bookstore
+```
+
+The contracts are available under `/contracts` folder.
+
+[[docker-server-side]]
+==== Server side (nodejs)
+
+Since we want to run tests, we could just execute:
+
+```bash
+$ npm test
+```
+
+however, for learning purposes, let's split it into pieces:
+
+```bash
+# Stop docker infra (nodejs, artifactory)
+$ ./stop_infra.sh
+# Start docker infra (nodejs, artifactory)
+$ ./setup_infra.sh
+
+# Kill & Run app
+$ pkill -f "node app"
+$ nohup node app &
+
+# Prepare environment variables
+$ SC_CONTRACT_DOCKER_VERSION="..."
+$ APP_IP="192.168.0.100"
+$ APP_PORT="3000"
+$ ARTIFACTORY_PORT="8081"
+$ APPLICATION_BASE_URL="http://${APP_IP}:${APP_PORT}"
+$ ARTIFACTORY_URL="http://${APP_IP}:${ARTIFACTORY_PORT}/artifactory/libs-release-local"
+$ CURRENT_DIR="$( pwd )"
+$ CURRENT_FOLDER_NAME=${PWD##*/}
+$ PROJECT_VERSION="0.0.1.RELEASE"
+
+# Execute contract tests
+$ docker run --rm -e "APPLICATION_BASE_URL=${APPLICATION_BASE_URL}" -e "PUBLISH_ARTIFACTS=true" -e "PROJECT_NAME=${CURRENT_FOLDER_NAME}" -e "REPO_WITH_BINARIES_URL=${ARTIFACTORY_URL}" -e "PROJECT_VERSION=${PROJECT_VERSION}" -v "${CURRENT_DIR}/contracts/:/contracts:ro" -v "${CURRENT_DIR}/node_modules/spring-cloud-contract/output:/spring-cloud-contract-output/" spring-cloud/spring-cloud-contract:"${SC_CONTRACT_DOCKER_VERSION}"
+
+# Kill app
+$ pkill -f "node app"
+```
+
+What will happen is that via bash scripts:
+
+- infrastructure will be set up (MongoDb, Artifactory).
+In real life scenario you would just run the NodeJS application
+with mocked database. In this example we want to show how we can
+benefit from Spring Cloud Contract in no time.
+- due to those constraints the contracts also represent the
+stateful situation
+** first request is a `POST` that causes data to get inserted to the database
+** second request is a `GET` that returns a list of data with 1 previously inserted element
+- the NodeJS application will be started (on port `3000`)
+- contract tests will be generated via Docker and tests
+will be executed against the running application
+** the contracts will be taken from `/contracts` folder.
+** the output of the test execution is available under
+`node_modules/spring-cloud-contract/output`.
+- the stubs will be uploaded to Artifactory. You can check them out
+under http://localhost:8081/artifactory/libs-release-local/com/example/bookstore/0.0.1.RELEASE/ .
+The stubs will be here http://localhost:8081/artifactory/libs-release-local/com/example/bookstore/0.0.1.RELEASE/bookstore-0.0.1.RELEASE-stubs.jar.
+
+To see how the client side looks like check out the <> section.
\ No newline at end of file
diff --git a/docs/src/main/asciidoc/verifier_stubrunner.adoc b/docs/src/main/asciidoc/verifier_stubrunner.adoc
index 5962cf615f..b867c771f8 100644
--- a/docs/src/main/asciidoc/verifier_stubrunner.adoc
+++ b/docs/src/main/asciidoc/verifier_stubrunner.adoc
@@ -112,3 +112,67 @@ IMPORTANT: Starting with version 1.0.4, you can provide a range of versions that
would like the Stub Runner to take into consideration. You can read more about the
https://wiki.eclipse.org/Aether/New_and_Noteworthy#Version_Ranges[Aether versioning
ranges here].
+
+[[stubrunner-docker]]
+=== Stub Runner Docker
+
+We're publishing a `spring-cloud/spring-cloud-contract-stub-runner` Docker image
+that will start the standalone version of Stub Runner.
+
+If you want to learn more about the basics of Maven, artifact ids,
+group ids, classifiers and Artifact Managers, just click here <>.
+
+==== How to use it
+
+Just execute the docker image. You can pass any of the <>
+as environment variables. The convention is that all the
+letters should be upper case. The camel case notation should
+and the dot (`.`) should be separated via underscore (`_`). E.g.
+ the `stubrunner.repositoryRoot` property should be represented
+ as a `STUBRUNNER_REPOSITORY_ROOT` environment variable.
+
+==== Example of client side usage in a non JVM project
+
+We'd like to use the stubs created in this <> step.
+Let's assume that we want to run the stubs on port `9876`. The NodeJS code
+is available here:
+
+```bash
+$ git clone https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs
+$ cd bookstore
+```
+
+Let's run the Stub Runner Boot application with the stubs.
+
+```bash
+# Provide the Spring Cloud Contract Docker version
+$ SC_CONTRACT_DOCKER_VERSION="..."
+# The IP at which the app is running and Docker container can reach it
+$ APP_IP="192.168.0.100"
+# Spring Cloud Contract Stub Runner properties
+$ STUBRUNNER_PORT="8083"
+# Stub coordinates 'groupId:artifactId:version:classifier:port'
+$ STUBRUNNER_IDS="com.example:bookstore:0.0.1.RELEASE:stubs:9876"
+$ STUBRUNNER_REPOSITORY_ROOT="http://${APP_IP}:8081/artifactory/libs-release-local"
+# Run the docker with Stub Runner Boot
+$ docker run --rm -e "STUBRUNNER_IDS=${STUBRUNNER_IDS}" -e "STUBRUNNER_REPOSITORY_ROOT=${STUBRUNNER_REPOSITORY_ROOT}" -p "${STUBRUNNER_PORT}:${STUBRUNNER_PORT}" -p "9876:9876" spring-cloud/spring-cloud-contract-stub-runner:"${SC_CONTRACT_DOCKER_VERSION}"
+```
+
+What's happening is that
+
+- a standalone Stub Runner application got started
+- it downloaded the stub with coordinates `com.example:bookstore:0.0.1.RELEASE:stubs` on port `9876`
+- it got downloaded from Artifactory running at `http://192.168.0.100:8081/artifactory/libs-release-local`
+- after a while Stub Runner will be running on port `8083`
+- and the stubs will be running at port `9876`
+
+On the server side we built a stateful stub. Let's use curl to assert
+that the stubs are setup properly.
+
+```bash
+# let's execute the first request (no response is returned)
+$ curl -H "Content-Type:application/json" -X POST --data '{ "title" : "Title", "genre" : "Genre", "description" : "Description", "author" : "Author", "publisher" : "Publisher", "pages" : 100, "image_url" : "https://d213dhlpdb53mu.cloudfront.net/assets/pivotal-square-logo-41418bd391196c3022f3cd9f3959b3f6d7764c47873d858583384e759c7db435.svg", "buy_url" : "https://pivotal.io" }' http://localhost:9876/api/books
+# Now time for the second request
+$ curl -X GET http://localhost:9876/api/books
+# You will receive contents of the JSON
+```
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 9d9f6bbf9e..f8dddca532 100644
--- a/pom.xml
+++ b/pom.xml
@@ -37,6 +37,7 @@
spring-cloud-contract-dependencies
docs
+ docker
spring-cloud-contract-shade
spring-cloud-contract-wiremock
spring-cloud-contract-verifier
@@ -44,6 +45,7 @@
spring-cloud-contract-stub-runner
spring-cloud-contract-starters
spring-cloud-contract-tools
+ spring-cloud-contract-stub-runner-boot
tests
samples
diff --git a/spring-cloud-contract-dependencies/pom.xml b/spring-cloud-contract-dependencies/pom.xml
index 1e573d1a9d..e35d952a06 100644
--- a/spring-cloud-contract-dependencies/pom.xml
+++ b/spring-cloud-contract-dependencies/pom.xml
@@ -50,6 +50,11 @@
spring-cloud-contract-stub-runner
${project.version}
+
+ org.springframework.cloud
+ spring-cloud-contract-stub-runner-boot
+ ${project.version}
+
org.springframework.cloud
spring-cloud-contract-shade
diff --git a/spring-cloud-contract-stub-runner-boot/.gitignore b/spring-cloud-contract-stub-runner-boot/.gitignore
new file mode 100644
index 0000000000..c71ea97aba
--- /dev/null
+++ b/spring-cloud-contract-stub-runner-boot/.gitignore
@@ -0,0 +1 @@
+/.apt_generated/
diff --git a/spring-cloud-contract-stub-runner-boot/README.adoc b/spring-cloud-contract-stub-runner-boot/README.adoc
new file mode 100644
index 0000000000..1cedb81239
--- /dev/null
+++ b/spring-cloud-contract-stub-runner-boot/README.adoc
@@ -0,0 +1,645 @@
+=== Stub Runner Core
+
+Runs stubs for service collaborators. Treating stubs as contracts of services allows to use stub-runner as an implementation of
+http://martinfowler.com/articles/consumerDrivenContracts.html[Consumer Driven Contracts].
+
+Stub Runner allows you to automatically download the stubs of the provided dependencies (or pick those from the classpath), start WireMock servers for them and feed them with proper stub definitions.
+For messaging, special stub routes are defined.
+
+==== Retrieving stubs
+
+You can pick the following options of acquiring stubs
+
+- Aether based solution that downloads JARs with stubs from Artifactory / Nexus
+- Classpath scanning solution that searches classpath via pattern to retrieve stubs
+- Write your own implementation of the `org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder` for full customization
+
+The latter example is described in the <> section.
+
+===== Stub downloading
+
+If you provide the `stubrunner.repositoryRoot` or `stubrunner.workOffline` flag will be set
+to `true` then Stub Runner will connect to the given server and download the required jars.
+It will then unpack the JAR to a temporary folder and reference those files in further
+contract processing.
+
+Example:
+
+[source,java]
+----
+@AutoConfigureStubRunner(repositoryRoot="http://foo.bar", ids = "com.example:beer-api-producer:+:stubs:8095")
+----
+
+===== Classpath scanning
+
+If you *DON'T* provide the `stubrunner.repositoryRoot` and `stubrunner.workOffline` flag will
+be set to `false` (that's the default) then classpath will get scanned. Let's look at the
+following example:
+
+[source,java]
+----
+@AutoConfigureStubRunner(ids = {
+ "com.example:beer-api-producer:+:stubs:8095",
+ "com.example.foo:bar:1.0.0:superstubs:8096"
+})
+----
+
+If you've added the dependencies to your classpath
+
+[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
+.Maven
+----
+
+ com.example
+ beer-api-producer-restdocs
+ stubs
+ 0.0.1-SNAPSHOT
+ test
+
+
+ *
+ *
+
+
+
+
+ com.example.foo
+ bar
+ superstubs
+ 1.0.0
+ test
+
+
+ *
+ *
+
+
+
+----
+
+[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
+.Gradle
+----
+testCompile("com.example:beer-api-producer-restdocs:0.0.1-SNAPSHOT:stubs") {
+ transitive = false
+}
+testCompile("com.example.foo:bar:1.0.0:superstubs") {
+ transitive = false
+}
+----
+
+Then the following locations on your classpath will get scanned. For `com.example:beer-api-producer-restdocs`
+
+- /META-INF/com.example/beer-api-producer-restdocs/**/*.*
+- /contracts/com.example/beer-api-producer-restdocs/**/*.*
+- /mappings/com.example/beer-api-producer-restdocs/**/*.*
+
+and `com.example.foo:bar`
+
+- /META-INF/com.example.foo/bar/**/*.*
+- /contracts/com.example.foo/bar/**/*.*
+- /mappings/com.example.foo/bar/**/*.*
+
+TIP: As you can see you have to explicitly provide the group and artifact ids when packaging the
+producer stubs.
+
+The producer would setup the contracts like this:
+
+[source,bash]
+----
+└── src
+ └── test
+ └── resources
+ └── contracts
+ └── com.example
+ └── beer-api-producer-restdocs
+ └── nested
+ └── contract3.groovy
+
+----
+
+To achieve proper stub packaging.
+
+Or using the https://github.com/spring-cloud-samples/spring-cloud-contract-samples/blob/master/producer_with_restdocs/pom.xml[Maven `assembly` plugin] or
+https://github.com/spring-cloud-samples/spring-cloud-contract-samples/blob/master/producer_with_restdocs/build.gradle[Gradle Jar] task you have to create the following
+structure in your stubs jar.
+
+[source,bash]
+----
+└── META-INF
+ └── com.example
+ └── beer-api-producer-restdocs
+ └── 2.0.0
+ ├── contracts
+ │ └── nested
+ │ └── contract2.groovy
+ └── mappings
+ └── mapping.json
+
+----
+
+By maintaining this structure classpath gets scanned and you can profit from the messaging /
+HTTP stubs without the need to download artifacts.
+
+==== Running stubs
+
+===== Limitations
+
+IMPORTANT: There might be a problem with StubRunner shutting down ports between tests. You might
+ have a situation in which you get port conflicts. As long as you use the same context across tests
+ everything works fine. But when the context are different (e.g. different stubs or different profiles)
+ then you have to either use `@DirtiesContext` to shut down the stub servers, or else run them on
+ different ports per test.
+
+===== Running using main app
+
+You can set the following options to the main class:
+
+[source,groovy,indent=0]
+----
+-c, --classifier Suffix for the jar containing stubs (e.
+ g. 'stubs' if the stub jar would
+ have a 'stubs' classifier for stubs:
+ foobar-stubs ). Defaults to 'stubs'
+ (default: stubs)
+--maxPort, --maxp Maximum port value to be assigned to
+ the WireMock instance. Defaults to
+ 15000 (default: 15000)
+--minPort, --minp Minimum port value to be assigned to
+ the WireMock instance. Defaults to
+ 10000 (default: 10000)
+-p, --password Password to user when connecting to
+ repository
+--phost, --proxyHost Proxy host to use for repository
+ requests
+--pport, --proxyPort [Integer] Proxy port to use for repository
+ requests
+-r, --root Location of a Jar containing server
+ where you keep your stubs (e.g. http:
+ //nexus.
+ net/content/repositories/repository)
+-s, --stubs Comma separated list of Ivy
+ representation of jars with stubs.
+ Eg. groupid:artifactid1,groupid2:
+ artifactid2:classifier
+-u, --username Username to user when connecting to
+ repository
+--wo, --workOffline Switch to work offline. Defaults to
+ 'false'
+----
+
+===== HTTP Stubs
+
+Stubs are defined in JSON documents, whose syntax is defined in http://wiremock.org/stubbing.html[WireMock documentation]
+
+Example:
+
+[source,javascript,indent=0]
+----
+{
+ "request": {
+ "method": "GET",
+ "url": "/ping"
+ },
+ "response": {
+ "status": 200,
+ "body": "pong",
+ "headers": {
+ "Content-Type": "text/plain"
+ }
+ }
+}
+----
+
+===== Viewing registered mappings
+
+Every stubbed collaborator exposes list of defined mappings under `__/admin/` endpoint.
+
+You can also use the `mappingsOutputFolder` property to dump the mappings to files.
+ For annotation based approach it would look like this
+
+[source,java]
+----
+@AutoConfigureStubRunner(ids="a.b.c:loanIssuance,a.b.c:fraudDetectionServer",
+mappingsOutputFolder = "target/outputmappings/")
+----
+
+and for the JUnit approach like this:
+
+[source,java]
+----
+@ClassRule @Shared StubRunnerRule rule = new StubRunnerRule()
+ .repoRoot("http://some_url")
+ .downloadStub("a.b.c", "loanIssuance")
+ .downloadStub("a.b.c:fraudDetectionServer")
+ .withMappingsOutputFolder("target/outputmappings")
+----
+
+Then if you check out the folder `target/outputmappings` you would see the following structure
+
+[source,bash]
+----
+.
+├── fraudDetectionServer_13705
+└── loanIssuance_12255
+----
+
+That means that there were two stubs registered. `fraudDetectionServer` was registered at port `13705`
+and `loanIssuance` at port `12255`. If we take a look at one of the files we would see (for WireMock)
+mappings available for the given server:
+
+[source,json]
+----
+[{
+ "id" : "f9152eb9-bf77-4c38-8289-90be7d10d0d7",
+ "request" : {
+ "url" : "/name",
+ "method" : "GET"
+ },
+ "response" : {
+ "status" : 200,
+ "body" : "fraudDetectionServer"
+ },
+ "uuid" : "f9152eb9-bf77-4c38-8289-90be7d10d0d7"
+},
+...
+]
+----
+
+===== Messaging Stubs
+
+Depending on the provided Stub Runner dependency and the DSL the messaging routes are automatically set up.
+
+=== Stub Runner JUnit Rule
+
+Stub Runner comes with a JUnit rule thanks to which you can very easily download and run stubs for given group and artifact id:
+
+[source,java,indent=0]
+----
+include::src/test/groovy/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRuleJUnitTest.java[tags=classrule]
+----
+
+After that rule gets executed Stub Runner connects to your Maven repository and for the given list of dependencies tries to:
+
+- download them
+- cache them locally
+- unzip them to a temporary folder
+- start a WireMock server for each Maven dependency on a random port from the provided range of ports / provided port
+- feed the WireMock server with all JSON files that are valid WireMock definitions
+- can also send messages (remember to pass an implementation of `MessageVerifier` interface)
+
+Stub Runner uses https://wiki.eclipse.org/Aether[Eclipse Aether] mechanism to download the Maven dependencies.
+Check their https://wiki.eclipse.org/Aether[docs] for more information.
+
+Since the `StubRunnerRule` implements the `StubFinder` it allows you to find the started stubs:
+
+[source,groovy,indent=0]
+----
+include::src/main/java/org/springframework/cloud/contract/stubrunner/StubFinder.java[lines=16..-1]
+----
+
+Example of usage in Spock tests:
+
+[source,groovy,indent=0]
+----
+include::src/test/groovy/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRuleSpec.groovy[tags=classrule]
+----
+
+Example of usage in JUnit tests:
+
+[source,java,indent=0]
+----
+include::src/test/groovy/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRuleJUnitTest.java[tags=test]
+----
+
+Check the *Common properties for JUnit and Spring* for more information on how to apply global configuration of Stub Runner.
+
+IMPORTANT: To use the JUnit rule together with messaging you have to provide an implementation of the
+`MessageVerifier` interface to the rule builder (e.g. `rule.messageVerifier(new MyMessageVerifier())`).
+If you don't do this then whenever you try to send a message an exception will be thrown.
+
+==== Maven settings
+
+The stub downloader honors Maven settings for a different local repository folder.
+Authentication details for repositories and profiles are currently not taken into account, so you need to specify it using the properties mentioned above.
+
+==== Providing fixed ports
+
+You can also run your stubs on fixed ports. You can do it in two different ways. One is to pass it in the properties, and the other via fluent API of
+JUnit rule.
+
+==== Fluent API
+
+When using the `StubRunnerRule` you can add a stub to download and then pass the port for the last downloaded stub.
+
+[source,java,indent=0]
+----
+include::src/test/groovy/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRuleCustomPortJUnitTest.java[tags=classrule_with_port]
+----
+
+You can see that for this example the following test is valid:
+
+[source,java,indent=0]
+----
+include::src/test/groovy/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRuleCustomPortJUnitTest.java[tags=test_with_port]
+----
+
+==== Stub Runner with Spring
+
+Sets up Spring configuration of the Stub Runner project.
+
+By providing a list of stubs inside your configuration file the Stub Runner automatically downloads
+and registers in WireMock the selected stubs.
+
+If you want to find the URL of your stubbed dependency you can autowire the `StubFinder` interface and use
+its methods as presented below:
+
+[source,groovy,indent=0]
+----
+include::src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfigurationSpec.groovy[tags=test]
+----
+
+for the following configuration file:
+
+[source,yml,indent=0]
+----
+include::src/test/resources/application-test.yml[tags=test]
+----
+
+Instead of using the properties you can also use the properties inside the `@AutoConfigureStubRunner`.
+Below you can find an example of achieving the same result by setting values on the annotation.
+
+[source,groovy,indent=0]
+----
+include::src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerSpringCloudAutoConfigurationSpec.groovy[tags=autoconfigure]
+----
+
+Stub Runner Spring registers environment variables in the following manner
+for every registered WireMock server. Example for Stub Runner ids
+ `com.example:foo`, `com.example:bar`.
+
+- `stubrunner.runningstubs.foo.port`
+- `stubrunner.runningstubs.bar.port`
+
+Which you can reference in your code.
+
+=== Stub Runner Spring Cloud
+
+Stub Runner can integrate with Spring Cloud.
+
+For real life examples you can check the
+
+ - https://github.com/spring-cloud-samples/spring-cloud-contract-samples/tree/master/producer[producer app sample]
+ - https://github.com/spring-cloud-samples/spring-cloud-contract-samples/tree/master/consumer_with_discovery[consumer app sample]
+
+==== Stubbing Service Discovery
+
+The most important feature of `Stub Runner Spring Cloud` is the fact that it's stubbing
+
+- `DiscoveryClient`
+- `Ribbon` `ServerList`
+
+that means that regardless of the fact whether you're using Zookeeper, Consul, Eureka or anything else, you don't need that in your tests.
+We're starting WireMock instances of your dependencies and we're telling your application whenever you're using `Feign`, load balanced `RestTemplate`
+or `DiscoveryClient` directly, to call those stubbed servers instead of calling the real Service Discovery tool.
+
+For example this test will pass
+
+[source,groovy,indent=0]
+----
+include::src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerSpringCloudAutoConfigurationSpec.groovy[tags=test]
+----
+
+for the following configuration file
+
+[source,yml,indent=0]
+----
+include::src/test/resources/application.yml[tags=ids]
+----
+
+===== Test profiles and service discovery
+
+In your integration tests you typically don't want to call neither a discovery service (e.g. Eureka)
+or Config Server. That's why you create an additional test configuration in which you want to disable
+these features.
+
+Due to certain limitations of https://github.com/spring-cloud/spring-cloud-commons/issues/156[`spring-cloud-commons`] to achieve this you have disable these properties
+via a static block like presented below (example for Eureka)
+
+[source,java]
+----
+ //Hack to work around https://github.com/spring-cloud/spring-cloud-commons/issues/156
+ static {
+ System.setProperty("eureka.client.enabled", "false");
+ System.setProperty("spring.cloud.config.failFast", "false");
+ }
+----
+
+==== Additional Configuration
+
+You can match the artifactId of the stub with the name of your app by using the `stubrunner.idsToServiceIds:` map.
+You can disable Stub Runner Ribbon support by providing: `stubrunner.cloud.ribbon.enabled` equal to `false`
+You can disable Stub Runner support by providing: `stubrunner.cloud.enabled` equal to `false`
+
+TIP: By default all service discovery will be stubbed. That means that regardless of the fact if you have
+an existing `DiscoveryClient` its results will be ignored. However, if you want to reuse it, just set
+ `stubrunner.cloud.delegate.enabled` to `true` and then your existing `DiscoveryClient` results will be
+ merged with the stubbed ones.
+
+=== Stub Runner Boot Application
+
+Spring Cloud Contract Stub Runner Boot is a Spring Boot application that exposes REST endpoints to
+trigger the messaging labels and to access started WireMock servers.
+
+One of the use-cases is to run some smoke (end to end) tests on a deployed application.
+You can check out the https://github.com/spring-cloud/spring-cloud-pipelines[Spring Cloud Pipelines]
+project for more information.
+
+==== How to use it?
+
+===== Stub Runner Server
+
+Just add the
+
+[source,groovy,indent=0]
+----
+compile "org.springframework.cloud:spring-cloud-starter-stub-runner"
+----
+
+Annotate a class with `@EnableStubRunnerServer`, build a fat-jar and you're ready to go!
+
+For the properties check the *Stub Runner Spring* section.
+
+===== Spring Cloud CLI
+
+Starting from `1.4.0.RELEASE` version of the http://cloud.spring.io/spring-cloud-cli[Spring Cloud CLI]
+project you can start Stub Runner Boot by executing `spring cloud stubrunner`.
+
+In order to pass the configuration just create a `stubrunner.yml` file in the current working directory
+or a subdirectory called `config` or in `~/.spring-cloud`. The file could look like this
+(example for running stubs installed locally)
+
+.stubrunner.yml
+[source,yml,indent=0]
+----
+stubrunner:
+ workOffline: true
+ ids:
+ - com.example:beer-api-producer:+:9876
+----
+
+and then just call `spring cloud stubrunner` from your terminal window to start
+the Stub Runner server. It will be available at port `8750`.
+
+==== Endpoints
+
+===== HTTP
+
+- GET `/stubs` - returns a list of all running stubs in `ivy:integer` notation
+- GET `/stubs/{ivy}` - returns a port for the given `ivy` notation (when calling the endpoint `ivy` can also be `artifactId` only)
+
+===== Messaging
+
+For Messaging
+
+- GET `/triggers` - returns a list of all running labels in `ivy : [ label1, label2 ...]` notation
+- POST `/triggers/{label}` - executes a trigger with `label`
+- POST `/triggers/{ivy}/{label}` - executes a trigger with `label` for the given `ivy` notation (when calling the endpoint `ivy` can also be `artifactId` only)
+
+==== Example
+
+[source,groovy,indent=0]
+----
+include::src/test/groovy/org/springframework/cloud/contract/stubrunner/server/StubRunnerBootSpec.groovy[tags=boot_usage]
+----
+
+==== Stub Runner Boot with Service Discovery
+
+One of the possibilities of using Stub Runner Boot is to use it as a feed of stubs for "smoke-tests". What does it mean?
+ Let's assume that you don't want to deploy 50 microservice to a test environment in order
+ to check if your application is working fine. You've already executed a suite of tests during the build process
+ but you would also like to ensure that the packaging of your application is fine. What you can do
+ is to deploy your application to an environment, start it and run a couple of tests on it to see if
+ it's working fine. We can call those tests smoke-tests since their idea is to check only a handful
+ of testing scenarios.
+
+The problem with this approach is such that if you're doing microservices most likely you're
+ using a service discovery tool. Stub Runner Boot allows you to solve this issue by starting the
+ required stubs and register them in a service discovery tool. Let's take a look at an example of
+ such a setup with Eureka. Let's assume that Eureka was already running.
+
+[source,java,indent=0]
+----
+include::src/test/groovy/org/springframework/cloud/contract/stubrunner/serverexamples/StubRunnerBootEurekaExample.java[tags=stubrunnereureka]
+----
+
+As you can see we want to start a Stub Runner Boot server `@EnableStubRunnerServer`, enable Eureka client `@EnableEurekaClient`
+and we want to have the stub runner feature turned on `@AutoConfigureStubRunner`.
+
+Now let's assume that we want to start this application so that the stubs get automatically registered.
+ We can do it by running the app `java -jar ${SYSTEM_PROPS} stub-runner-boot-eureka-example.jar` where
+ `${SYSTEM_PROPS}` would contain the following list of properties
+
+[source,bash,indent=0]
+----
+include::src/test/groovy/org/springframework/cloud/contract/stubrunner/serverexamples/StubRunnerBootEurekaExample.java[tags=stubrunnereureka_args]
+----
+
+That way your deployed application can send requests to started WireMock servers via the service
+discovery. Most likely points 1-3 could be set by default in `application.yml` cause they are not
+likely to change. That way you can provide only the list of stubs to download whenever you start
+the Stub Runner Boot.
+
+=== Stubs Per Consumer
+
+There are cases in which 2 consumers of the same endpoint want to have 2 different responses.
+
+TIP: This approach also allows you to immediately know which consumer is using which part of your API.
+You can remove part of a response that your API produces and you can see which of your autogenerated tests
+fails. If none fails then you can safely delete that part of the response cause nobody is using it.
+
+Let's look at the following example for contract defined for the producer called `producer`.
+There are 2 consumers: `foo-consumer` and `bar-consumer`.
+
+*Consumer `foo-service`*
+
+[source,groovy]
+----
+request {
+ url '/foo'
+ method GET()
+}
+response {
+ status 200
+ body(
+ foo: "foo"
+ }
+}
+----
+
+*Consumer `bar-service`*
+
+[source,groovy]
+----
+request {
+ url '/foo'
+ method GET()
+}
+response {
+ status 200
+ body(
+ bar: "bar"
+ }
+}
+----
+
+You can't produce for the same request 2 different responses. That's why you can properly package the
+contracts and then profit from the `stubsPerConsumer` feature.
+
+On the producer side the consumers can have a folder that contains contracts related only to them.
+By setting the `stubrunner.stubs-per-consumer` flag to `true` we no longer register all stubs but only those that
+correspond to the consumer application's name. In other words we'll scan the path of every stub and
+if it contains the subfolder with name of the consumer in the path only then will it get registered.
+
+On the `foo` producer side the contracts would look like this
+
+[source,bash]
+----
+.
+└── contracts
+ ├── bar-consumer
+ │ ├── bookReturnedForBar.groovy
+ │ └── shouldCallBar.groovy
+ └── foo-consumer
+ ├── bookReturnedForFoo.groovy
+ └── shouldCallFoo.groovy
+----
+
+Being the `bar-consumer` consumer you can either set the `spring.application.name` or the `stubrunner.consumer-name` to `bar-consumer`
+Or set the test as follows:
+
+[source,groovy]
+----
+include::src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerStubsPerConsumerSpec.groovy[tags=test]
+...
+}
+----
+
+Then only the stubs registered under a path that contains the `bar-consumer` in its name (i.e. those from the
+`src/test/resources/contracts/bar-consumer/some/contracts/...` folder) will be allowed to be referenced.
+
+Or set the consumer name explicitly
+
+[source,groovy]
+----
+include::src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerStubsPerConsumerWithConsumerNameSpec.groovy[tags=test]
+...
+}
+----
+
+Then only the stubs registered under a path that contains the `foo-consumer` in its name (i.e. those from the
+`src/test/resources/contracts/foo-consumer/some/contracts/...` folder) will be allowed to be referenced.
+
+You can check out https://github.com/spring-cloud/spring-cloud-contract/issues/224[issue 224] for more
+information about the reasons behind this change.
+
diff --git a/spring-cloud-contract-stub-runner-boot/pom.xml b/spring-cloud-contract-stub-runner-boot/pom.xml
new file mode 100644
index 0000000000..8fd515ca8f
--- /dev/null
+++ b/spring-cloud-contract-stub-runner-boot/pom.xml
@@ -0,0 +1,29 @@
+
+
+ 4.0.0
+
+ org.springframework.cloud
+ spring-cloud-contract-parent
+ 1.2.3.BUILD-SNAPSHOT
+ ..
+
+ spring-cloud-contract-stub-runner-boot
+ jar
+ Spring Cloud Contract Stub Runner Boot
+ Spring Cloud Contract Stub Runner Boot
+
+
+ org.springframework.cloud
+ spring-cloud-starter-contract-stub-runner
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/spring-cloud-contract-stub-runner-boot/src/main/java/org/springframework/cloud/contract/stubrunner/server/StubRunnerBoot.java b/spring-cloud-contract-stub-runner-boot/src/main/java/org/springframework/cloud/contract/stubrunner/server/StubRunnerBoot.java
new file mode 100644
index 0000000000..d68dd87906
--- /dev/null
+++ b/spring-cloud-contract-stub-runner-boot/src/main/java/org/springframework/cloud/contract/stubrunner/server/StubRunnerBoot.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2013-2017 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.contract.stubrunner.server;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * @author Marcin Grzejszczak
+ */
+@SpringBootApplication
+@EnableStubRunnerServer
+public class StubRunnerBoot {
+
+ public static void main(String[] args) {
+ SpringApplication.run(StubRunnerBoot.class, args);
+ }
+
+}