From 9b15e6b5a3c0fc18372f532063bb43f25abcf984 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 16 Mar 2017 21:07:45 +0000 Subject: [PATCH 01/30] Use Gradle, executed by Maven, to build the Gradle plugin --- .../spring-boot-gradle-plugin/build.gradle | 69 +++++++ .../gradle.properties | 1 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54208 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + .../spring-boot-gradle-plugin/gradlew | 172 ++++++++++++++++++ .../spring-boot-gradle-plugin/gradlew.bat | 84 +++++++++ .../spring-boot-gradle-plugin/pom.xml | 140 ++++++++------ 7 files changed, 418 insertions(+), 54 deletions(-) create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/build.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/gradle.properties create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/gradle/wrapper/gradle-wrapper.jar create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/gradle/wrapper/gradle-wrapper.properties create mode 100755 spring-boot-tools/spring-boot-gradle-plugin/gradlew create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/gradlew.bat diff --git a/spring-boot-tools/spring-boot-gradle-plugin/build.gradle b/spring-boot-tools/spring-boot-gradle-plugin/build.gradle new file mode 100644 index 00000000000..7de2a073463 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/build.gradle @@ -0,0 +1,69 @@ +plugins { + id 'java' + id 'eclipse' +} + +repositories { + mavenLocal() +} + +def addDependency(configurationName, dependency) { + def coordinates = [ + 'group': dependency.groupId.text(), + 'name': dependency.artifactId.text(), + 'version': dependency.version.text() + ] + dependencies { + add configurationName, coordinates + } +} + +def effectivePomFile = file('target/effective-pom.xml') +if (effectivePomFile.file) { + def pom = new XmlSlurper().parseText(file('target/effective-pom.xml').text) + pom.dependencies.dependency.each { dependency -> + def scope = dependency.scope.text() + if (scope == 'compile') { + addDependency scope, dependency + } + else if (scope == 'test') { + addDependency 'testCompile', dependency + } + } +} + +dependencies { + compile localGroovy() + compile gradleApi() +} + +jar { + manifest { + attributes 'Implementation-Version': (version ? version : 'unknown') + } +} + + +eclipseJdt { + inputFile = rootProject.file('../../eclipse/org.eclipse.jdt.core.prefs') + doLast { + project.file('.settings/org.eclipse.jdt.ui.prefs').withWriter { writer -> + writer << file('../../eclipse/org.eclipse.jdt.ui.prefs').text + } + } +} + +task sourcesJar(type: Jar) { + classifier = 'sources' + from sourceSets.main.allSource +} + +task javadocJar(type: Jar) { + classifier = "javadoc" + from javadoc +} + +artifacts { + archives sourcesJar + archives javadocJar +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/gradle.properties b/spring-boot-tools/spring-boot-gradle-plugin/gradle.properties new file mode 100644 index 00000000000..6b1823d86a6 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/gradle.properties @@ -0,0 +1 @@ +org.gradle.daemon=false diff --git a/spring-boot-tools/spring-boot-gradle-plugin/gradle/wrapper/gradle-wrapper.jar b/spring-boot-tools/spring-boot-gradle-plugin/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..52b668fd0f32fb4cd7288cec23da75b65c0e701a GIT binary patch literal 54208 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 \(.*\)$'` + 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/spring-boot-tools/spring-boot-gradle-plugin/gradlew.bat b/spring-boot-tools/spring-boot-gradle-plugin/gradlew.bat new file mode 100644 index 00000000000..f9553162f12 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/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/spring-boot-tools/spring-boot-gradle-plugin/pom.xml b/spring-boot-tools/spring-boot-gradle-plugin/pom.xml index b9134a3c447..e289a5b3803 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/pom.xml +++ b/spring-boot-tools/spring-boot-gradle-plugin/pom.xml @@ -7,6 +7,7 @@ 2.0.0.BUILD-SNAPSHOT spring-boot-gradle-plugin + pom Spring Boot Gradle Plugin Spring Boot Gradle Plugin http://projects.spring.io/spring-boot/ @@ -16,9 +17,9 @@ ${basedir}/../.. + ./gradlew - org.springframework.boot spring-boot-loader-tools @@ -27,58 +28,89 @@ io.spring.gradle dependency-management-plugin - - - org.codehaus.groovy - groovy - provided - - - org.gradle - gradle-base-services - provided - - - org.gradle - gradle-core - provided - - - org.gradle - gradle-language-java - provided - - - org.gradle - gradle-language-jvm - provided - - - org.gradle - gradle-platform-jvm - provided - - - org.gradle - gradle-plugins - provided - - - org.gradle - gradle-process-services - provided - - - - gradle - http://repo.gradle.org/gradle/libs-releases-local - - true - - - false - - - + + + + maven-help-plugin + + + prepare-package + + effective-pom + + + ${project.build.directory}/effective-pom.xml + + + + + + org.codehaus.mojo + exec-maven-plugin + + + gradle + prepare-package + + ${gradle.executable} + + clean + build + -Pversion=${project.version} + + + + exec + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 1.12 + + + attach-artifacts + package + + attach-artifact + + + + + build/libs/${project.artifactId}-${project.version}.jar + jar + + + build/libs/${project.artifactId}-${project.version}-javadoc.jar + jar + javadoc + + + build/libs/${project.artifactId}-${project.version}-sources.jar + jar + sources + + + + + + + + + + + windows + + + windows + + + + gradlew.bat + + + From 20fe95b2760111e024ad2e216989f4e486135361 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Sat, 11 Mar 2017 16:26:09 +0000 Subject: [PATCH 02/30] React to the Java plugin being applied rather than always applying it --- .../boot/gradle/plugin/SpringBootPlugin.java | 4 +--- .../boot/gradle/repackage/RepackagePluginFeatures.java | 8 +++++--- .../boot/gradle/run/RunPluginFeatures.java | 9 ++++++--- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java index 604ca6fe244..403a38cbbe8 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-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. @@ -20,7 +20,6 @@ import org.gradle.api.Action; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.Task; -import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.tasks.compile.JavaCompile; import org.springframework.boot.gradle.SpringBootPluginExtension; @@ -42,7 +41,6 @@ public class SpringBootPlugin implements Plugin { public void apply(Project project) { project.getExtensions().create("springBoot", SpringBootPluginExtension.class, project); - project.getPlugins().apply(JavaPlugin.class); new AgentPluginFeatures().apply(project); new RepackagePluginFeatures().apply(project); new RunPluginFeatures().apply(project); diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/repackage/RepackagePluginFeatures.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/repackage/RepackagePluginFeatures.java index b979ed4c7d8..f7a540874da 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/repackage/RepackagePluginFeatures.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/repackage/RepackagePluginFeatures.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-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. @@ -53,8 +53,10 @@ public class RepackagePluginFeatures implements PluginFeatures { @Override public void apply(Project project) { - addRepackageTask(project); - registerRepackageTaskProperty(project); + project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> { + addRepackageTask(project); + registerRepackageTaskProperty(project); + }); } private void addRepackageTask(Project project) { diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/RunPluginFeatures.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/RunPluginFeatures.java index ad56178980a..4ac2b1e507a 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/RunPluginFeatures.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/RunPluginFeatures.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-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. @@ -23,6 +23,7 @@ import org.gradle.api.Action; import org.gradle.api.Project; import org.gradle.api.Task; import org.gradle.api.plugins.ExtraPropertiesExtension; +import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPluginConvention; import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.application.CreateStartScripts; @@ -42,8 +43,10 @@ public class RunPluginFeatures implements PluginFeatures { @Override public void apply(Project project) { - mainClassNameFinder(project); - addBootRunTask(project); + project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> { + mainClassNameFinder(project); + addBootRunTask(project); + }); } private void mainClassNameFinder(Project project) { From 2ce85569761aafa4087e0f4dc3c956ff9fd5219a Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 2 Mar 2017 16:46:27 +0000 Subject: [PATCH 03/30] Introduce bootJar and bootWar tasks for creating fat jars and wars Previously, the BootRepackage task would take the output of a Jar or War task and repackage it in a similar manner to Spring Boot's Maven plugin. This caused several problems in Gradle including broken up-to-date checks and a lack of configurability. See the issues referenced below for full details. This commit replaces BootRepackage with BootJar and BootWar for building executable jars and wars respectively. BootJar extends Gradle's standard Jar task and BootWar extends Gradle's standard War task. This means that terms of configuration, the creation of executable jars and wars is now as flexible as the creation of standards jars and wars. Closes gh-8167 Closes gh-8099 Closes gh-6846 Closes gh-5861 Closes gh-5393 Closes gh-5259 Closes gh-3931 --- .../spring-boot-gradle-plugin/build.gradle | 1 + .../boot/gradle/bundling/BootArchive.java | 102 ++++++ .../gradle/bundling/BootArchiveSupport.java | 94 +++++ .../boot/gradle/bundling/BootJar.java | 119 +++++++ .../boot/gradle/bundling/BootWar.java | 119 +++++++ .../gradle/bundling/BootZipCopyAction.java | 243 +++++++++++++ .../bundling/BundlingPluginFeatures.java | 67 ++++ .../bundling/LaunchScriptConfiguration.java | 116 ++++++ .../gradle/bundling/MainClassSupplier.java | 72 ++++ .../boot/gradle/plugin/SpringBootPlugin.java | 4 +- .../gradle/repackage/ProjectLibraries.java | 334 ------------------ .../repackage/RepackagePluginFeatures.java | 174 --------- .../boot/gradle/repackage/RepackageTask.java | 322 ----------------- .../AbstractBootArchiveIntegrationTests.java | 100 ++++++ .../bundling/AbstractBootArchiveTests.java | 234 ++++++++++++ .../bundling/BootJarIntegrationTests.java | 30 ++ .../boot/gradle/bundling/BootJarTests.java | 31 ++ .../bundling/BootWarIntegrationTests.java | 30 ++ .../boot/gradle/bundling/BootWarTests.java | 50 +++ .../boot/gradle/testkit/GradleBuild.java | 113 ++++++ .../bundling/BootJarIntegrationTests.gradle | 16 + .../bundling/BootWarIntegrationTests.gradle | 16 + 22 files changed, 1555 insertions(+), 832 deletions(-) create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchive.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchiveSupport.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootJar.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootWar.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootZipCopyAction.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BundlingPluginFeatures.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/LaunchScriptConfiguration.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/MainClassSupplier.java delete mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/repackage/ProjectLibraries.java delete mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/repackage/RepackagePluginFeatures.java delete mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/repackage/RepackageTask.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveIntegrationTests.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveTests.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootJarIntegrationTests.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootJarTests.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootWarIntegrationTests.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootWarTests.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/BootJarIntegrationTests.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/BootWarIntegrationTests.gradle diff --git a/spring-boot-tools/spring-boot-gradle-plugin/build.gradle b/spring-boot-tools/spring-boot-gradle-plugin/build.gradle index 7de2a073463..5bef3dcec65 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/build.gradle +++ b/spring-boot-tools/spring-boot-gradle-plugin/build.gradle @@ -35,6 +35,7 @@ if (effectivePomFile.file) { dependencies { compile localGroovy() compile gradleApi() + testCompile gradleTestKit() } jar { diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchive.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchive.java new file mode 100644 index 00000000000..46c8db3cd50 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchive.java @@ -0,0 +1,102 @@ +/* + * Copyright 2012-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.boot.gradle.bundling; + +import org.gradle.api.Action; +import org.gradle.api.Project; +import org.gradle.api.Task; +import org.gradle.api.file.FileCollection; +import org.gradle.api.file.FileTreeElement; +import org.gradle.api.specs.Spec; +import org.gradle.api.tasks.Classpath; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.Optional; + +/** + * A Spring Boot "fat" archive task. + * + * @author Andy Wilkinson + */ +public interface BootArchive extends Task { + + /** + * Returns the main class of the application. + * + * @return the main class + */ + @Input + @Optional + String getMainClass(); + + /** + * Sets the main class of the application. + * + * @param mainClass the main class of the application + */ + void setMainClass(String mainClass); + + /** + * Adds Ant-style patterns that identify files that must be unpacked from the archive + * when it is launched. + * + * @param patterns the patterns + */ + void requiresUnpack(String... patterns); + + /** + * Adds a spec that identifies files that must be unpacked from the archive when it is + * launched. + * + * @param spec the spec + */ + void requiresUnpack(Spec spec); + + /** + * Returns the {@link LaunchScriptConfiguration} that will control the script, if any, + * that is prepended to the archive. + * + * @return the launch script configuration + */ + @Input + LaunchScriptConfiguration getLaunchScript(); + + /** + * Applies the given {@code action} to the {@link LaunchScriptConfiguration} of this + * archive. + * + * @param action the action to apply + */ + void launchScript(Action action); + + /** + * Returns the classpath that will be included in the archive. + * + * @return the classpath + */ + @Optional + @Classpath + FileCollection getClasspath(); + + /** + * Adds files to the classpath to include in the archive. The given {@code classpath} + * are evaluated as per {@link Project#files(Object...)}. + * + * @param classpath the additions to the classpath + */ + void classpath(Object... classpath); + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchiveSupport.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchiveSupport.java new file mode 100644 index 00000000000..2dbc1040a9c --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchiveSupport.java @@ -0,0 +1,94 @@ +/* + * Copyright 2012-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.boot.gradle.bundling; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.gradle.api.file.FileTreeElement; +import org.gradle.api.internal.file.copy.CopyAction; +import org.gradle.api.java.archives.Attributes; +import org.gradle.api.specs.Spec; +import org.gradle.api.specs.Specs; +import org.gradle.api.tasks.bundling.Jar; +import org.gradle.api.tasks.util.PatternSet; + +/** + * Support class for implementations of {@link BootArchive}. + * + * @author Andy Wilkinson + */ +class BootArchiveSupport { + + private final PatternSet requiresUnpack = new PatternSet(); + + private final MainClassSupplier mainClassSupplier; + + private final Set storedPathPrefixes; + + private String loaderMainClass; + + private LaunchScriptConfiguration launchScript = new LaunchScriptConfiguration(); + + BootArchiveSupport(MainClassSupplier mainClassSupplier, + String... storedPathPrefixes) { + this.mainClassSupplier = mainClassSupplier; + this.storedPathPrefixes = new HashSet<>(Arrays.asList(storedPathPrefixes)); + this.requiresUnpack.include(Specs.satisfyNone()); + } + + void configureManifest(Jar jar) { + Attributes attributes = jar.getManifest().getAttributes(); + attributes.putIfAbsent("Main-Class", this.loaderMainClass); + attributes.putIfAbsent("Start-Class", this.mainClassSupplier.get()); + } + + CopyAction createCopyAction(Jar jar) { + return new BootZipCopyAction(jar.getArchivePath(), this::requiresUnpacking, + this.launchScript, this.storedPathPrefixes); + } + + private boolean requiresUnpacking(FileTreeElement fileTreeElement) { + return this.requiresUnpack.getAsSpec().isSatisfiedBy(fileTreeElement); + } + + String getLoaderMainClass() { + return this.loaderMainClass; + } + + void setLoaderMainClass(String loaderMainClass) { + this.loaderMainClass = loaderMainClass; + } + + LaunchScriptConfiguration getLaunchScript() { + return this.launchScript; + } + + void setLaunchScript(LaunchScriptConfiguration launchScript) { + this.launchScript = launchScript; + } + + void requiresUnpack(String... patterns) { + this.requiresUnpack.include(patterns); + } + + void requiresUnpack(Spec spec) { + this.requiresUnpack.include(spec); + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootJar.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootJar.java new file mode 100644 index 00000000000..3a118c8e6e8 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootJar.java @@ -0,0 +1,119 @@ +/* + * Copyright 2012-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.boot.gradle.bundling; + +import java.io.File; +import java.util.Collections; +import java.util.concurrent.Callable; + +import org.gradle.api.Action; +import org.gradle.api.file.CopySpec; +import org.gradle.api.file.FileCollection; +import org.gradle.api.file.FileTreeElement; +import org.gradle.api.internal.file.copy.CopyAction; +import org.gradle.api.specs.Spec; +import org.gradle.api.tasks.bundling.Jar; + +/** + * A custom {@link Jar} task that produces a Spring Boot executable jar. + * + * @author Andy Wilkinson + */ +public class BootJar extends Jar implements BootArchive { + + private final MainClassSupplier mainClassSupplier = new MainClassSupplier( + this::getClasspath); + + private BootArchiveSupport support = new BootArchiveSupport(this.mainClassSupplier, "BOOT-INF/lib"); + + private FileCollection classpath; + + private String mainClass; + + public BootJar() { + this.support.setLoaderMainClass("org.springframework.boot.loader.JarLauncher"); + CopySpec bootInf = getRootSpec().addChildBeforeSpec(getMainSpec()) + .into("BOOT-INF"); + bootInf.into("lib", classpathFiles(File::isFile)); + bootInf.into("classes", classpathFiles(File::isDirectory)); + } + + private Action classpathFiles(Spec filter) { + return (copySpec) -> { + copySpec.from((Callable>) () -> { + return this.classpath == null ? Collections.emptyList() + : this.classpath.filter(filter); + }); + }; + } + + @Override + public void copy() { + this.support.configureManifest(this); + super.copy(); + } + + @Override + protected CopyAction createCopyAction() { + return this.support.createCopyAction(this); + } + + @Override + public String getMainClass() { + return this.mainClass; + } + + @Override + public void setMainClass(String mainClass) { + this.mainClass = mainClass; + this.mainClassSupplier.setMainClass(mainClass); + } + + @Override + public void requiresUnpack(String... patterns) { + this.support.requiresUnpack(patterns); + } + + @Override + public void requiresUnpack(Spec spec) { + this.support.requiresUnpack(spec); + } + + @Override + public LaunchScriptConfiguration getLaunchScript() { + return this.support.getLaunchScript(); + } + + @Override + public void launchScript(Action action) { + action.execute(getLaunchScript()); + } + + @Override + public FileCollection getClasspath() { + return this.classpath; + } + + @Override + public void classpath(Object... classpath) { + FileCollection existingClasspath = this.classpath; + this.classpath = getProject().files( + existingClasspath == null ? Collections.emptyList() : existingClasspath, + classpath); + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootWar.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootWar.java new file mode 100644 index 00000000000..dddebe0777a --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootWar.java @@ -0,0 +1,119 @@ +/* + * Copyright 2012-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.boot.gradle.bundling; + +import java.io.File; +import java.util.Collections; +import java.util.concurrent.Callable; + +import org.gradle.api.Action; +import org.gradle.api.Project; +import org.gradle.api.file.FileCollection; +import org.gradle.api.file.FileTreeElement; +import org.gradle.api.internal.file.copy.CopyAction; +import org.gradle.api.specs.Spec; +import org.gradle.api.tasks.Optional; +import org.gradle.api.tasks.bundling.War; + +/** + * A custom {@link War} task that produces a Spring Boot executable war. + * + * @author Andy Wilkinson + */ +public class BootWar extends War implements BootArchive { + + private final MainClassSupplier mainClassSupplier = new MainClassSupplier( + this::getClasspath); + + private final BootArchiveSupport support = new BootArchiveSupport( + this.mainClassSupplier, "WEB-INF/lib/", "WEB-INF/lib-provided"); + + private String mainClass; + + private FileCollection providedClasspath; + + public BootWar() { + this.support.setLoaderMainClass("org.springframework.boot.loader.WarLauncher"); + getWebInf().into("lib-provided", (copySpec) -> { + copySpec.from((Callable>) () -> { + return this.providedClasspath == null ? Collections.emptyList() + : this.providedClasspath; + }); + }); + } + + @Override + public void copy() { + this.support.configureManifest(this); + super.copy(); + } + + @Override + protected CopyAction createCopyAction() { + return this.support.createCopyAction(this); + } + + @Override + public String getMainClass() { + return this.mainClass; + } + + @Override + public void setMainClass(String mainClass) { + this.mainClass = mainClass; + this.mainClassSupplier.setMainClass(mainClass); + } + + @Override + public void requiresUnpack(String... patterns) { + this.support.requiresUnpack(patterns); + } + + @Override + public void requiresUnpack(Spec spec) { + this.support.requiresUnpack(spec); + } + + @Override + public LaunchScriptConfiguration getLaunchScript() { + return this.support.getLaunchScript(); + } + + @Override + public void launchScript(Action action) { + action.execute(getLaunchScript()); + } + + @Optional + public FileCollection getProvidedClasspath() { + return this.providedClasspath; + } + + /** + * Adds files to the provided classpath to include in the war. The given + * {@code classpath} are evaluated as per {@link Project#files(Object...)}. + * + * @param classpath the additions to the classpath + */ + public void providedClasspath(Object... classpath) { + FileCollection existingClasspath = this.providedClasspath; + this.providedClasspath = getProject().files( + existingClasspath == null ? Collections.emptyList() : existingClasspath, + classpath); + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootZipCopyAction.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootZipCopyAction.java new file mode 100644 index 00000000000..652c12ad6dd --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootZipCopyAction.java @@ -0,0 +1,243 @@ +/* + * Copyright 2012-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.boot.gradle.bundling; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Set; +import java.util.zip.CRC32; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +import org.gradle.api.GradleException; +import org.gradle.api.file.FileTreeElement; +import org.gradle.api.internal.file.CopyActionProcessingStreamAction; +import org.gradle.api.internal.file.copy.CopyAction; +import org.gradle.api.internal.file.copy.CopyActionProcessingStream; +import org.gradle.api.internal.file.copy.FileCopyDetailsInternal; +import org.gradle.api.specs.Spec; +import org.gradle.api.tasks.WorkResult; + +import org.springframework.boot.loader.tools.DefaultLaunchScript; +import org.springframework.boot.loader.tools.FileUtils; + +/** + * A {@link CopyAction} for creating a Spring Boot zip archive (typically a jar or war). + * Stores jar files without compression as required by Spring Boot's loader. + * + * @author Andy Wilkinson + */ +class BootZipCopyAction implements CopyAction { + + private final File output; + + private final Spec requiresUnpack; + + private final LaunchScriptConfiguration launchScript; + + private final Set storedPathPrefixes; + + BootZipCopyAction(File output, Spec requiresUnpack, + LaunchScriptConfiguration launchScript, Set storedPathPrefixes) { + this.output = output; + this.requiresUnpack = requiresUnpack; + this.launchScript = launchScript; + this.storedPathPrefixes = storedPathPrefixes; + } + + @Override + public WorkResult execute(CopyActionProcessingStream stream) { + ZipOutputStream zipStream; + try { + FileOutputStream fileStream = new FileOutputStream(this.output); + writeLaunchScriptIfNecessary(fileStream); + zipStream = new ZipOutputStream(fileStream); + writeLoaderClasses(zipStream); + } + catch (IOException ex) { + throw new GradleException("Failed to create " + this.output, ex); + } + try { + stream.process(new ZipStreamAction(zipStream, this.output, + this.requiresUnpack, this.storedPathPrefixes)); + } + finally { + try { + zipStream.close(); + } + catch (IOException ex) { + // Continue + } + } + return () -> { + return true; + }; + } + + private void writeLoaderClasses(ZipOutputStream out) { + + ZipEntry entry; + try (ZipInputStream in = new ZipInputStream(getClass() + .getResourceAsStream("/META-INF/loader/spring-boot-loader.jar"))) { + byte[] buffer = new byte[4096]; + while ((entry = in.getNextEntry()) != null) { + if (entry.getName().endsWith((".class"))) { + out.putNextEntry(entry); + int read; + while ((read = in.read(buffer)) > 0) { + out.write(buffer, 0, read); + } + out.closeEntry(); + } + } + } + catch (IOException ex) { + throw new GradleException("Failed to write loader classes", ex); + } + } + + private void writeLaunchScriptIfNecessary(FileOutputStream fileStream) { + try { + if (this.launchScript.isIncluded()) { + fileStream.write(new DefaultLaunchScript(this.launchScript.getScript(), + this.launchScript.getProperties()).toByteArray()); + } + } + catch (IOException ex) { + throw new GradleException("Failed to write launch script to " + this.output, + ex); + } + } + + private static final class ZipStreamAction + implements CopyActionProcessingStreamAction { + + private final ZipOutputStream zipStream; + + private final File output; + + private final Spec requiresUnpack; + + private final Set storedPathPrefixes; + + private ZipStreamAction(ZipOutputStream zipStream, File output, + Spec requiresUnpack, Set storedPathPrefixes) { + this.zipStream = zipStream; + this.output = output; + this.requiresUnpack = requiresUnpack; + this.storedPathPrefixes = storedPathPrefixes; + } + + @Override + public void processFile(FileCopyDetailsInternal details) { + try { + if (details.isDirectory()) { + createDirectory(details); + } + else { + createFile(details); + } + } + catch (IOException ex) { + throw new GradleException( + "Failed to add " + details + " to " + this.output, ex); + } + } + + private void createDirectory(FileCopyDetailsInternal details) throws IOException { + ZipEntry archiveEntry = new ZipEntry( + details.getRelativePath().getPathString() + '/'); + archiveEntry.setTime(details.getLastModified()); + this.zipStream.putNextEntry(archiveEntry); + this.zipStream.closeEntry(); + } + + private void createFile(FileCopyDetailsInternal details) throws IOException { + String relativePath = details.getRelativePath().getPathString(); + ZipEntry archiveEntry = new ZipEntry(relativePath); + archiveEntry.setTime(details.getLastModified()); + this.zipStream.putNextEntry(archiveEntry); + if (isStoredEntry(relativePath)) { + archiveEntry.setMethod(ZipEntry.STORED); + archiveEntry.setSize(details.getSize()); + archiveEntry.setCompressedSize(details.getSize()); + Crc32OutputStream crcStream = new Crc32OutputStream(this.zipStream); + details.copyTo(crcStream); + archiveEntry.setCrc(crcStream.getCrc()); + if (this.requiresUnpack.isSatisfiedBy(details)) { + archiveEntry.setComment( + "UNPACK:" + FileUtils.sha1Hash(details.getFile())); + } + } + else { + details.copyTo(this.zipStream); + } + this.zipStream.closeEntry(); + } + + private boolean isStoredEntry(String relativePath) { + for (String prefix : this.storedPathPrefixes) { + if (relativePath.startsWith(prefix)) { + return true; + } + } + return false; + } + + } + + /** + * A {@code FilterOutputStream} that provides a CRC-32 of the data that is written to + * it. + */ + private static final class Crc32OutputStream extends FilterOutputStream { + + private final CRC32 crc32 = new CRC32(); + + private Crc32OutputStream(OutputStream out) { + super(out); + } + + @Override + public void write(int b) throws IOException { + this.crc32.update(b); + this.out.write(b); + } + + @Override + public void write(byte[] b) throws IOException { + this.crc32.update(b); + this.out.write(b); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + this.crc32.update(b, off, len); + this.out.write(b, off, len); + } + + private long getCrc() { + return this.crc32.getValue(); + } + + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BundlingPluginFeatures.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BundlingPluginFeatures.java new file mode 100644 index 00000000000..f49ff1bf90c --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BundlingPluginFeatures.java @@ -0,0 +1,67 @@ +/* + * Copyright 2012-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.boot.gradle.bundling; + +import java.util.concurrent.Callable; + +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.file.FileCollection; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.plugins.JavaPluginConvention; +import org.gradle.api.plugins.WarPlugin; +import org.gradle.api.tasks.SourceSet; + +import org.springframework.boot.gradle.PluginFeatures; + +/** + * {@link PluginFeatures} for the bundling of an application. + * + * @author Andy Wilkinson + */ +public class BundlingPluginFeatures implements PluginFeatures { + + @Override + public void apply(Project project) { + project.getPlugins().withType(JavaPlugin.class, + (javaPlugin) -> configureBootJarTask(project)); + project.getPlugins().withType(WarPlugin.class, + (warPlugin) -> configureBootWarTask(project)); + } + + private void configureBootWarTask(Project project) { + BootWar bootWar = project.getTasks().create("bootWar", BootWar.class); + bootWar.providedClasspath(providedRuntimeConfiguration(project)); + } + + private void configureBootJarTask(Project project) { + BootJar bootJar = project.getTasks().create("bootJar", BootJar.class); + bootJar.classpath((Callable) () -> { + JavaPluginConvention convention = project.getConvention() + .getPlugin(JavaPluginConvention.class); + SourceSet mainSourceSet = convention.getSourceSets() + .getByName(SourceSet.MAIN_SOURCE_SET_NAME); + return mainSourceSet.getRuntimeClasspath(); + }); + } + + private Configuration providedRuntimeConfiguration(Project project) { + return project.getConfigurations() + .getByName(WarPlugin.PROVIDED_RUNTIME_CONFIGURATION_NAME); + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/LaunchScriptConfiguration.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/LaunchScriptConfiguration.java new file mode 100644 index 00000000000..f302cc0f91f --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/LaunchScriptConfiguration.java @@ -0,0 +1,116 @@ +/* + * Copyright 2012-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.boot.gradle.bundling; + +import java.io.File; +import java.io.IOException; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.boot.loader.tools.FileUtils; + +/** + * Encapsulates the configuration of the launch script for an executable jar or war. + * + * @author Andy Wilkinson + */ +public class LaunchScriptConfiguration implements Serializable { + + private boolean included = false; + + private final Map properties = new HashMap(); + + private File script; + + public boolean isIncluded() { + return this.included; + } + + public void setIncluded(boolean included) { + this.included = included; + } + + public Map getProperties() { + return this.properties; + } + + public void properties(Map properties) { + this.properties.putAll(properties); + } + + public File getScript() { + return this.script; + } + + public void setScript(File script) { + this.script = script; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (this.included ? 1231 : 1237); + result = prime * result + + ((this.properties == null) ? 0 : this.properties.hashCode()); + result = prime * result + ((this.script == null) ? 0 : this.script.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + LaunchScriptConfiguration other = (LaunchScriptConfiguration) obj; + if (this.included != other.included) { + return false; + } + if (!this.properties.equals(other.properties)) { + return false; + } + if (this.script == null) { + if (other.script != null) { + return false; + } + } + else if (!this.script.equals(other.script)) { + return false; + } + else if (!equalContents(this.script, other.script)) { + return false; + } + return true; + } + + private boolean equalContents(File one, File two) { + try { + return FileUtils.sha1Hash(one).equals(FileUtils.sha1Hash(two)); + } + catch (IOException ex) { + return false; + } + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/MainClassSupplier.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/MainClassSupplier.java new file mode 100644 index 00000000000..1e6a76b4eb7 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/MainClassSupplier.java @@ -0,0 +1,72 @@ +/* + * Copyright 2012-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.boot.gradle.bundling; + +import java.io.File; +import java.io.IOException; +import java.util.function.Supplier; + +import org.gradle.api.file.FileCollection; + +import org.springframework.boot.loader.tools.MainClassFinder; + +/** + * Supplies the main class for an application by returning a configured main class if + * available. If a main class is not available, directories in the application's classpath + * are searched. + * + * @author Andy Wilkinson + */ +class MainClassSupplier implements Supplier { + + private final Supplier classpathSupplier; + + private String mainClass; + + MainClassSupplier(Supplier classpathSupplier) { + this.classpathSupplier = classpathSupplier; + } + + @Override + public String get() { + if (this.mainClass != null) { + return this.mainClass; + } + return findMainClass(); + } + + private String findMainClass() { + FileCollection classpath = this.classpathSupplier.get(); + return classpath == null ? null + : classpath.filter(File::isDirectory).getFiles().stream() + .map(this::findMainClass).findFirst().orElse(null); + } + + private String findMainClass(File file) { + try { + return MainClassFinder.findSingleMainClass(file); + } + catch (IOException ex) { + return null; + } + } + + void setMainClass(String mainClass) { + this.mainClass = mainClass; + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java index 403a38cbbe8..0879b8c3d11 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java @@ -24,8 +24,8 @@ import org.gradle.api.tasks.compile.JavaCompile; import org.springframework.boot.gradle.SpringBootPluginExtension; import org.springframework.boot.gradle.agent.AgentPluginFeatures; +import org.springframework.boot.gradle.bundling.BundlingPluginFeatures; import org.springframework.boot.gradle.dependencymanagement.DependencyManagementPluginFeatures; -import org.springframework.boot.gradle.repackage.RepackagePluginFeatures; import org.springframework.boot.gradle.run.RunPluginFeatures; /** @@ -42,7 +42,7 @@ public class SpringBootPlugin implements Plugin { project.getExtensions().create("springBoot", SpringBootPluginExtension.class, project); new AgentPluginFeatures().apply(project); - new RepackagePluginFeatures().apply(project); + new BundlingPluginFeatures().apply(project); new RunPluginFeatures().apply(project); new DependencyManagementPluginFeatures().apply(project); project.getTasks().withType(JavaCompile.class).all(new SetUtf8EncodingAction()); diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/repackage/ProjectLibraries.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/repackage/ProjectLibraries.java deleted file mode 100644 index 1ebd3a6c331..00000000000 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/repackage/ProjectLibraries.java +++ /dev/null @@ -1,334 +0,0 @@ -/* - * Copyright 2012-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.boot.gradle.repackage; - -import java.io.File; -import java.io.IOException; -import java.lang.reflect.Method; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.Set; - -import org.gradle.api.Project; -import org.gradle.api.artifacts.Configuration; -import org.gradle.api.artifacts.Dependency; -import org.gradle.api.artifacts.FileCollectionDependency; -import org.gradle.api.artifacts.ModuleVersionIdentifier; -import org.gradle.api.artifacts.ProjectDependency; -import org.gradle.api.artifacts.ResolvedArtifact; - -import org.springframework.boot.gradle.SpringBootPluginExtension; -import org.springframework.boot.loader.tools.Libraries; -import org.springframework.boot.loader.tools.Library; -import org.springframework.boot.loader.tools.LibraryCallback; -import org.springframework.boot.loader.tools.LibraryScope; - -/** - * Expose Gradle {@link Configuration}s as {@link Libraries}. - * - * @author Phillip Webb - * @author Andy Wilkinson - */ -class ProjectLibraries implements Libraries { - - private final Project project; - - private final SpringBootPluginExtension extension; - - private final boolean excludeDevtools; - - private final TargetConfigurationResolver targetConfigurationResolver; - - private String providedConfigurationName = "providedRuntime"; - - private String customConfigurationName = null; - - /** - * Create a new {@link ProjectLibraries} instance of the specified {@link Project}. - * @param project the gradle project - * @param extension the extension - * @param excludeDevTools whether Spring Boot Devtools should be excluded - */ - ProjectLibraries(Project project, SpringBootPluginExtension extension, - boolean excludeDevTools) { - this.project = project; - this.extension = extension; - this.excludeDevtools = excludeDevTools; - this.targetConfigurationResolver = createTargetConfigurationResolver(); - } - - private static TargetConfigurationResolver createTargetConfigurationResolver() { - try { - return new Gradle3TargetConfigurationResolver( - ProjectDependency.class.getMethod("getTargetConfiguration")); - } - catch (Exception ex) { - return new Gradle2TargetConfigurationResolver(); - } - } - - /** - * Set the name of the provided configuration. Defaults to 'providedRuntime'. - * @param providedConfigurationName the providedConfigurationName to set - */ - public void setProvidedConfigurationName(String providedConfigurationName) { - this.providedConfigurationName = providedConfigurationName; - } - - public void setCustomConfigurationName(String customConfigurationName) { - this.customConfigurationName = customConfigurationName; - } - - @Override - public void doWithLibraries(LibraryCallback callback) throws IOException { - Set custom = getLibraries(this.customConfigurationName, - LibraryScope.CUSTOM); - if (custom != null) { - libraries(custom, callback); - } - else { - Set runtime = getLibraries("runtime", LibraryScope.RUNTIME); - Set provided = getLibraries(this.providedConfigurationName, - LibraryScope.PROVIDED); - if (provided != null) { - runtime = minus(runtime, provided); - } - libraries(runtime, callback); - libraries(provided, callback); - } - } - - private Set getLibraries(String configurationName, - LibraryScope scope) { - Configuration configuration = (configurationName == null ? null - : this.project.getConfigurations().findByName(configurationName)); - if (configuration == null) { - return null; - } - Set libraries = new LinkedHashSet<>(); - for (ResolvedArtifact artifact : configuration.getResolvedConfiguration() - .getResolvedArtifacts()) { - libraries.add(new ResolvedArtifactLibrary(artifact, scope)); - } - libraries.addAll(getLibrariesForFileDependencies(configuration, scope)); - return libraries; - } - - private Set getLibrariesForFileDependencies( - Configuration configuration, LibraryScope scope) { - Set libraries = new LinkedHashSet<>(); - for (Dependency dependency : configuration.getIncoming().getDependencies()) { - if (dependency instanceof FileCollectionDependency) { - FileCollectionDependency fileDependency = (FileCollectionDependency) dependency; - for (File file : fileDependency.resolve()) { - libraries.add( - new GradleLibrary(fileDependency.getGroup(), file, scope)); - } - } - else if (dependency instanceof ProjectDependency) { - ProjectDependency projectDependency = (ProjectDependency) dependency; - libraries - .addAll(getLibrariesForFileDependencies( - this.targetConfigurationResolver - .resolveTargetConfiguration(projectDependency), - scope)); - } - } - return libraries; - } - - private Set minus(Set source, - Set toRemove) { - if (source == null || toRemove == null) { - return source; - } - Set filesToRemove = new HashSet<>(); - for (GradleLibrary library : toRemove) { - filesToRemove.add(library.getFile()); - } - Set result = new LinkedHashSet<>(); - for (GradleLibrary library : source) { - if (!filesToRemove.contains(library.getFile())) { - result.add(library); - } - } - return result; - } - - private void libraries(Set libraries, LibraryCallback callback) - throws IOException { - if (libraries != null) { - Set duplicates = getDuplicates(libraries); - for (GradleLibrary library : libraries) { - if (!isExcluded(library)) { - library.setIncludeGroupName(duplicates.contains(library.getName())); - callback.library(library); - } - } - } - } - - private boolean isExcluded(GradleLibrary library) { - if (this.excludeDevtools && isDevToolsJar(library)) { - return true; - } - return false; - } - - private boolean isDevToolsJar(GradleLibrary library) { - return "org.springframework.boot".equals(library.getGroup()) - && library.getName().startsWith("spring-boot-devtools"); - } - - private Set getDuplicates(Set libraries) { - Set duplicates = new HashSet<>(); - Set seen = new HashSet<>(); - for (GradleLibrary library : libraries) { - if (library.getFile() != null && !seen.add(library.getFile().getName())) { - duplicates.add(library.getFile().getName()); - } - } - return duplicates; - } - - private class GradleLibrary extends Library { - - private final String group; - - private boolean includeGroupName; - - GradleLibrary(String group, File file, LibraryScope scope) { - super(file, scope); - this.group = group; - } - - public void setIncludeGroupName(boolean includeGroupName) { - this.includeGroupName = includeGroupName; - } - - public String getGroup() { - return this.group; - } - - @Override - public String getName() { - String name = super.getName(); - if (this.includeGroupName && this.group != null) { - name = this.group + "-" + name; - } - return name; - } - - @Override - public int hashCode() { - return getFile().hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof GradleLibrary) { - return getFile().equals(((GradleLibrary) obj).getFile()); - } - return false; - } - - @Override - public String toString() { - return getFile().getAbsolutePath(); - } - - } - - /** - * Adapts a {@link ResolvedArtifact} to a {@link Library}. - */ - private class ResolvedArtifactLibrary extends GradleLibrary { - - private final ResolvedArtifact artifact; - - ResolvedArtifactLibrary(ResolvedArtifact artifact, LibraryScope scope) { - super(artifact.getModuleVersion().getId().getGroup(), artifact.getFile(), - scope); - this.artifact = artifact; - } - - @Override - public boolean isUnpackRequired() { - if (ProjectLibraries.this.extension.getRequiresUnpack() != null) { - ModuleVersionIdentifier id = this.artifact.getModuleVersion().getId(); - return ProjectLibraries.this.extension.getRequiresUnpack() - .contains(id.getGroup() + ":" + id.getName()); - } - return false; - } - - } - - /** - * Strategy used to resolve configurations regardless of the underlying Gradle - * version. - */ - private interface TargetConfigurationResolver { - - Configuration resolveTargetConfiguration(ProjectDependency projectDependency); - - } - - /** - * {@link TargetConfigurationResolver} for Gradle 2.x. - */ - private static final class Gradle2TargetConfigurationResolver - implements TargetConfigurationResolver { - - @Override - public Configuration resolveTargetConfiguration( - ProjectDependency projectDependency) { - return projectDependency.getProjectConfiguration(); - } - - } - - /** - * {@link TargetConfigurationResolver} for Gradle 3.x. - */ - private static final class Gradle3TargetConfigurationResolver - implements TargetConfigurationResolver { - - private final Method getTargetConfiguration; - - private Gradle3TargetConfigurationResolver(Method getTargetConfiguration) { - this.getTargetConfiguration = getTargetConfiguration; - } - - @Override - public Configuration resolveTargetConfiguration( - ProjectDependency projectDependency) { - try { - String configurationName = (String) this.getTargetConfiguration - .invoke(projectDependency); - return projectDependency.getDependencyProject().getConfigurations() - .getByName(configurationName == null - ? Dependency.DEFAULT_CONFIGURATION : configurationName); - } - catch (Exception ex) { - throw new RuntimeException("Failed to get target configuration", ex); - } - } - - } - -} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/repackage/RepackagePluginFeatures.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/repackage/RepackagePluginFeatures.java deleted file mode 100644 index f7a540874da..00000000000 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/repackage/RepackagePluginFeatures.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright 2012-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.boot.gradle.repackage; - -import java.io.File; -import java.io.IOException; - -import org.gradle.api.Action; -import org.gradle.api.Project; -import org.gradle.api.Task; -import org.gradle.api.artifacts.Configuration; -import org.gradle.api.artifacts.Dependency; -import org.gradle.api.logging.Logger; -import org.gradle.api.plugins.BasePlugin; -import org.gradle.api.plugins.JavaPlugin; -import org.gradle.api.tasks.TaskDependency; -import org.gradle.api.tasks.bundling.Jar; - -import org.springframework.boot.gradle.PluginFeatures; -import org.springframework.boot.gradle.SpringBootPluginExtension; -import org.springframework.boot.gradle.run.FindMainClassTask; -import org.springframework.boot.loader.tools.Library; -import org.springframework.boot.loader.tools.LibraryCallback; -import org.springframework.util.StringUtils; - -/** - * {@link PluginFeatures} to add repackage support. - * - * @author Phillip Webb - * @author Dave Syer - * @author Andy Wilkinson - */ -public class RepackagePluginFeatures implements PluginFeatures { - - /** - * The name of the repackage task. - */ - public static final String REPACKAGE_TASK_NAME = "bootRepackage"; - - @Override - public void apply(Project project) { - project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> { - addRepackageTask(project); - registerRepackageTaskProperty(project); - }); - } - - private void addRepackageTask(Project project) { - RepackageTask task = project.getTasks().create(REPACKAGE_TASK_NAME, - RepackageTask.class); - task.setDescription("Repackage existing JAR and WAR " - + "archives so that they can be executed from the command " - + "line using 'java -jar'"); - task.setGroup(BasePlugin.BUILD_GROUP); - Configuration runtimeConfiguration = project.getConfigurations() - .getByName(JavaPlugin.RUNTIME_CONFIGURATION_NAME); - TaskDependency runtimeProjectDependencyJarTasks = runtimeConfiguration - .getTaskDependencyFromProjectDependency(true, JavaPlugin.JAR_TASK_NAME); - task.dependsOn( - project.getConfigurations().getByName(Dependency.ARCHIVES_CONFIGURATION) - .getAllArtifacts().getBuildDependencies(), - runtimeProjectDependencyJarTasks); - registerOutput(project, task); - ensureTaskRunsOnAssembly(project, task); - ensureMainClassHasBeenFound(project, task); - } - - private void registerOutput(Project project, final RepackageTask task) { - project.afterEvaluate(new Action() { - @Override - public void execute(Project project) { - project.getTasks().withType(Jar.class, - new RegisterInputsOutputsAction(task)); - Object withJar = task.getWithJarTask(); - if (withJar != null) { - task.dependsOn(withJar); - } - } - }); - } - - private void ensureTaskRunsOnAssembly(Project project, Task task) { - project.getTasks().getByName(BasePlugin.ASSEMBLE_TASK_NAME).dependsOn(task); - } - - private void ensureMainClassHasBeenFound(Project project, Task task) { - task.dependsOn(project.getTasks().withType(FindMainClassTask.class)); - } - - /** - * Register BootRepackage so that we can use task {@code foo(type: BootRepackage)}. - * @param project the source project - */ - private void registerRepackageTaskProperty(Project project) { - project.getExtensions().getExtraProperties().set("BootRepackage", - RepackageTask.class); - } - - /** - * Register task input/outputs when classifiers are used. - */ - private static class RegisterInputsOutputsAction implements Action { - - private final RepackageTask task; - - private final Project project; - - RegisterInputsOutputsAction(RepackageTask task) { - this.task = task; - this.project = task.getProject(); - } - - @Override - public void execute(Jar jarTask) { - if ("".equals(jarTask.getClassifier())) { - String classifier = this.task.getClassifier(); - if (classifier == null) { - SpringBootPluginExtension extension = this.project.getExtensions() - .getByType(SpringBootPluginExtension.class); - classifier = extension.getClassifier(); - this.task.setClassifier(classifier); - } - if (classifier != null) { - setupInputOutputs(jarTask, classifier); - } - } - } - - private void setupInputOutputs(Jar jarTask, String classifier) { - Logger logger = this.project.getLogger(); - logger.debug("Using classifier: " + classifier + " for task " - + this.task.getName()); - File inputFile = jarTask.getArchivePath(); - String outputName = inputFile.getName(); - outputName = StringUtils.stripFilenameExtension(outputName) + "-" + classifier - + "." + StringUtils.getFilenameExtension(outputName); - File outputFile = new File(inputFile.getParentFile(), outputName); - this.task.getInputs().file(jarTask); - addLibraryDependencies(this.task); - this.task.getOutputs().file(outputFile); - this.task.setOutputFile(outputFile); - } - - private void addLibraryDependencies(final RepackageTask task) { - try { - task.getLibraries().doWithLibraries(new LibraryCallback() { - @Override - public void library(Library library) throws IOException { - task.getInputs().file(library.getFile()); - } - }); - } - catch (IOException ex) { - throw new IllegalStateException(ex); - } - } - - } - -} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/repackage/RepackageTask.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/repackage/RepackageTask.java deleted file mode 100644 index 155adebca2e..00000000000 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/repackage/RepackageTask.java +++ /dev/null @@ -1,322 +0,0 @@ -/* - * Copyright 2012-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.boot.gradle.repackage; - -import java.io.File; -import java.io.IOException; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import org.gradle.api.Action; -import org.gradle.api.DefaultTask; -import org.gradle.api.Project; -import org.gradle.api.Task; -import org.gradle.api.plugins.ExtraPropertiesExtension; -import org.gradle.api.tasks.TaskAction; -import org.gradle.api.tasks.bundling.Jar; - -import org.springframework.boot.gradle.SpringBootPluginExtension; -import org.springframework.boot.loader.tools.DefaultLaunchScript; -import org.springframework.boot.loader.tools.LaunchScript; -import org.springframework.boot.loader.tools.Layout; -import org.springframework.boot.loader.tools.Repackager; -import org.springframework.boot.loader.tools.Repackager.MainClassTimeoutWarningListener; -import org.springframework.util.FileCopyUtils; - -/** - * Repackage task. - * - * @author Phillip Webb - * @author Janne Valkealahti - * @author Andy Wilkinson - */ -public class RepackageTask extends DefaultTask { - - private String customConfiguration; - - private Object withJarTask; - - private String mainClass; - - private String classifier; - - private File outputFile; - - private Boolean excludeDevtools; - - private Boolean executable; - - private File embeddedLaunchScript; - - private Map embeddedLaunchScriptProperties; - - public void setCustomConfiguration(String customConfiguration) { - this.customConfiguration = customConfiguration; - } - - public Object getWithJarTask() { - return this.withJarTask; - } - - public void setWithJarTask(Object withJarTask) { - this.withJarTask = withJarTask; - } - - public void setMainClass(String mainClass) { - this.mainClass = mainClass; - } - - public String getMainClass() { - return this.mainClass; - } - - public String getClassifier() { - return this.classifier; - } - - public void setClassifier(String classifier) { - this.classifier = classifier; - } - - void setOutputFile(File file) { - this.outputFile = file; - } - - public Boolean getExcludeDevtools() { - return this.excludeDevtools; - } - - public void setExcludeDevtools(Boolean excludeDevtools) { - this.excludeDevtools = excludeDevtools; - } - - public Boolean getExecutable() { - return this.executable; - } - - public void setExecutable(Boolean executable) { - this.executable = executable; - } - - public File getEmbeddedLaunchScript() { - return this.embeddedLaunchScript; - } - - public void setEmbeddedLaunchScript(File embeddedLaunchScript) { - this.embeddedLaunchScript = embeddedLaunchScript; - } - - public Map getEmbeddedLaunchScriptProperties() { - return this.embeddedLaunchScriptProperties; - } - - public void setEmbeddedLaunchScriptProperties( - Map embeddedLaunchScriptProperties) { - this.embeddedLaunchScriptProperties = embeddedLaunchScriptProperties; - } - - @TaskAction - public void repackage() { - Project project = getProject(); - SpringBootPluginExtension extension = project.getExtensions() - .getByType(SpringBootPluginExtension.class); - ProjectLibraries libraries = getLibraries(); - project.getTasks().withType(Jar.class, new RepackageAction(extension, libraries)); - } - - public ProjectLibraries getLibraries() { - Project project = getProject(); - SpringBootPluginExtension extension = project.getExtensions() - .getByType(SpringBootPluginExtension.class); - ProjectLibraries libraries = new ProjectLibraries(project, extension, - this.excludeDevtools == null ? extension.isExcludeDevtools() - : this.excludeDevtools); - if (extension.getProvidedConfiguration() != null) { - libraries.setProvidedConfigurationName(extension.getProvidedConfiguration()); - } - if (this.customConfiguration != null) { - libraries.setCustomConfigurationName(this.customConfiguration); - } - else if (extension.getCustomConfiguration() != null) { - libraries.setCustomConfigurationName(extension.getCustomConfiguration()); - } - return libraries; - } - - /** - * Action to repackage JARs. - */ - private class RepackageAction implements Action { - - private final SpringBootPluginExtension extension; - - private final ProjectLibraries libraries; - - RepackageAction(SpringBootPluginExtension extension, ProjectLibraries libraries) { - this.extension = extension; - this.libraries = libraries; - } - - @Override - public void execute(Jar jarTask) { - if (!RepackageTask.this.isEnabled()) { - getLogger().info("Repackage disabled"); - return; - } - Object withJarTask = RepackageTask.this.withJarTask; - if (!isTaskMatch(jarTask, withJarTask)) { - getLogger().info( - "Jar task not repackaged (didn't match withJarTask): " + jarTask); - return; - } - File file = jarTask.getArchivePath(); - if (file.exists()) { - repackage(file); - } - } - - private boolean isTaskMatch(Jar task, Object withJarTask) { - if (withJarTask == null) { - if ("".equals(task.getClassifier())) { - Set tasksWithCustomRepackaging = new HashSet<>(); - for (RepackageTask repackageTask : RepackageTask.this.getProject() - .getTasks().withType(RepackageTask.class)) { - if (repackageTask.getWithJarTask() != null) { - tasksWithCustomRepackaging - .add(repackageTask.getWithJarTask()); - } - } - return !tasksWithCustomRepackaging.contains(task); - } - return false; - } - return task.equals(withJarTask) || task.getName().equals(withJarTask); - } - - private void repackage(File file) { - File outputFile = RepackageTask.this.outputFile; - if (outputFile != null && !file.equals(outputFile)) { - copy(file, outputFile); - file = outputFile; - } - Repackager repackager = new Repackager(file, - this.extension.getLayoutFactory()); - repackager.addMainClassTimeoutWarningListener( - new LoggingMainClassTimeoutWarningListener()); - setMainClass(repackager); - Layout layout = this.extension.convertLayout(); - if (layout != null) { - repackager.setLayout(layout); - } - repackager.setBackupSource(this.extension.isBackupSource()); - try { - LaunchScript launchScript = getLaunchScript(); - repackager.repackage(file, this.libraries, launchScript); - } - catch (IOException ex) { - throw new IllegalStateException(ex.getMessage(), ex); - } - } - - private void copy(File source, File dest) { - try { - FileCopyUtils.copy(source, dest); - } - catch (IOException ex) { - throw new IllegalStateException(ex.getMessage(), ex); - } - } - - private void setMainClass(Repackager repackager) { - String mainClassName = getMainClassNameProperty(); - if (RepackageTask.this.mainClass != null) { - mainClassName = RepackageTask.this.mainClass; - } - else if (this.extension.getMainClass() != null) { - mainClassName = this.extension.getMainClass(); - } - else { - Task runTask = getProject().getTasks().findByName("run"); - if (runTask != null && runTask.hasProperty("main")) { - mainClassName = (String) getProject().getTasks().getByName("run") - .property("main"); - } - } - if (mainClassName != null) { - getLogger().info("Setting mainClass: " + mainClassName); - repackager.setMainClass(mainClassName); - } - else { - getLogger().info("No mainClass configured"); - } - } - - private String getMainClassNameProperty() { - if (getProject().hasProperty("mainClassName")) { - return (String) getProject().property("mainClassName"); - } - ExtraPropertiesExtension extraProperties = (ExtraPropertiesExtension) getProject() - .getExtensions().getByName("ext"); - if (extraProperties.has("mainClassName")) { - return (String) extraProperties.get("mainClassName"); - } - return null; - } - - private LaunchScript getLaunchScript() throws IOException { - if (isExecutable() || getEmbeddedLaunchScript() != null) { - return new DefaultLaunchScript(getEmbeddedLaunchScript(), - getEmbeddedLaunchScriptProperties()); - } - return null; - } - - private boolean isExecutable() { - return RepackageTask.this.executable != null ? RepackageTask.this.executable - : this.extension.isExecutable(); - } - - private File getEmbeddedLaunchScript() { - return RepackageTask.this.embeddedLaunchScript != null - ? RepackageTask.this.embeddedLaunchScript - : this.extension.getEmbeddedLaunchScript(); - } - - private Map getEmbeddedLaunchScriptProperties() { - return RepackageTask.this.embeddedLaunchScriptProperties != null - ? RepackageTask.this.embeddedLaunchScriptProperties - : this.extension.getEmbeddedLaunchScriptProperties(); - } - - } - - /** - * {@link Repackager} that also logs when searching takes too long. - */ - private class LoggingMainClassTimeoutWarningListener - implements MainClassTimeoutWarningListener { - - @Override - public void handleTimeoutWarning(long duration, String mainMethod) { - getLogger().warn("Searching for the main-class is taking " - + "some time, consider using setting " + "'springBoot.mainClass'"); - } - - } - -} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveIntegrationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveIntegrationTests.java new file mode 100644 index 00000000000..9ab3e301c93 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveIntegrationTests.java @@ -0,0 +1,100 @@ +/* + * Copyright 2012-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.boot.gradle.bundling; + +import java.io.IOException; + +import org.gradle.testkit.runner.InvalidRunnerConfigurationException; +import org.gradle.testkit.runner.TaskOutcome; +import org.gradle.testkit.runner.UnexpectedBuildFailure; +import org.junit.Rule; +import org.junit.Test; + +import org.springframework.boot.gradle.testkit.GradleBuild; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for {@link BootJar}. + * + * @author Andy Wilkinson + */ +public abstract class AbstractBootArchiveIntegrationTests { + + @Rule + public final GradleBuild gradleBuild = new GradleBuild(); + + private final String taskName; + + protected AbstractBootArchiveIntegrationTests(String taskName) { + this.taskName = taskName; + } + + @Test + public void basicBuild() throws InvalidRunnerConfigurationException, + UnexpectedBuildFailure, IOException { + assertThat(this.gradleBuild.build(this.taskName).task(":" + this.taskName) + .getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + } + + @Test + public void upToDateWhenBuiltTwice() throws InvalidRunnerConfigurationException, + UnexpectedBuildFailure, IOException { + assertThat(this.gradleBuild.build(this.taskName).task(":" + this.taskName) + .getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + assertThat(this.gradleBuild.build(this.taskName).task(":" + this.taskName) + .getOutcome()).isEqualTo(TaskOutcome.UP_TO_DATE); + } + + @Test + public void upToDateWhenBuiltTwiceWithLaunchScriptIncluded() + throws InvalidRunnerConfigurationException, UnexpectedBuildFailure, + IOException { + assertThat(this.gradleBuild.build("-PincludeLaunchScript=true", this.taskName) + .task(":" + this.taskName).getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + assertThat(this.gradleBuild.build("-PincludeLaunchScript=true", this.taskName) + .task(":" + this.taskName).getOutcome()) + .isEqualTo(TaskOutcome.UP_TO_DATE); + } + + @Test + public void notUpToDateWhenLaunchScriptWasNotIncludedAndThenIsIncluded() { + assertThat(this.gradleBuild.build(this.taskName).task(":" + this.taskName) + .getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + assertThat(this.gradleBuild.build("-PincludeLaunchScript=true", this.taskName) + .task(":" + this.taskName).getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + } + + @Test + public void notUpToDateWhenLaunchScriptWasIncludedAndThenIsNotIncluded() { + assertThat(this.gradleBuild.build(this.taskName).task(":" + this.taskName) + .getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + assertThat(this.gradleBuild.build("-PincludeLaunchScript=true", this.taskName) + .task(":" + this.taskName).getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + } + + @Test + public void notUpToDateWhenLaunchScriptPropertyChanges() { + assertThat(this.gradleBuild.build("-PincludeLaunchScript=true", + "-PlaunchScriptProperty=foo", this.taskName).task(":" + this.taskName) + .getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + assertThat(this.gradleBuild.build("-PincludeLaunchScript=true", + "-PlaunchScriptProperty=bar", this.taskName).task(":" + this.taskName) + .getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveTests.java new file mode 100644 index 00000000000..000691d54d9 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveTests.java @@ -0,0 +1,234 @@ +/* + * Copyright 2012-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.boot.gradle.bundling; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.StandardOpenOption; +import java.util.Arrays; +import java.util.jar.JarFile; + +import org.gradle.api.Project; +import org.gradle.api.tasks.bundling.AbstractArchiveTask; +import org.gradle.api.tasks.bundling.Jar; +import org.gradle.testfixtures.ProjectBuilder; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import org.springframework.boot.loader.tools.DefaultLaunchScript; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Abstract base class for testing {@link BootArchive} implementations. + * + * @param the type of the concrete BootArchive implementation + * @author Andy Wilkinson + */ +public abstract class AbstractBootArchiveTests { + + @Rule + public final TemporaryFolder temp = new TemporaryFolder(); + + private final Class taskClass; + + private final String launcherClass; + + private final String libPath; + + private final String classesPath; + + private T task; + + protected AbstractBootArchiveTests(Class taskClass, String launcherClass, + String libPath, String classesPath) { + this.taskClass = taskClass; + this.launcherClass = launcherClass; + this.libPath = libPath; + this.classesPath = classesPath; + } + + @Before + public void createTask() { + try { + Project project = ProjectBuilder.builder() + .withProjectDir(this.temp.newFolder()).build(); + this.task = configure( + project.getTasks().create("testArchive", this.taskClass)); + } + catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + @Test + public void basicArchiveCreation() throws IOException { + this.task.setMainClass("com.example.Main"); + this.task.execute(); + assertThat(this.task.getArchivePath().exists()); + try (JarFile jarFile = new JarFile(this.task.getArchivePath())) { + assertThat(jarFile.getManifest().getMainAttributes().getValue("Main-Class")) + .isEqualTo(this.launcherClass); + assertThat(jarFile.getManifest().getMainAttributes().getValue("Start-Class")) + .isEqualTo("com.example.Main"); + } + } + + @Test + public void classpathJarsArePackagedBeneathLibPath() throws IOException { + this.task.setMainClass("com.example.Main"); + this.task.classpath(this.temp.newFile("one.jar"), this.temp.newFile("two.jar")); + this.task.execute(); + try (JarFile jarFile = new JarFile(this.task.getArchivePath())) { + assertThat(jarFile.getEntry(this.libPath + "/one.jar")).isNotNull(); + assertThat(jarFile.getEntry(this.libPath + "/two.jar")).isNotNull(); + } + } + + @Test + public void classpathFoldersArePackagedBeneathClassesPath() throws IOException { + this.task.setMainClass("com.example.Main"); + File classpathFolder = this.temp.newFolder(); + File applicationClass = new File(classpathFolder, + "com/example/Application.class"); + applicationClass.getParentFile().mkdirs(); + applicationClass.createNewFile(); + this.task.classpath(classpathFolder); + this.task.execute(); + try (JarFile jarFile = new JarFile(this.task.getArchivePath())) { + assertThat( + jarFile.getEntry(this.classesPath + "/com/example/Application.class")) + .isNotNull(); + } + } + + @Test + public void loaderIsWrittenToTheRootOfTheJar() throws IOException { + this.task.setMainClass("com.example.Main"); + this.task.execute(); + try (JarFile jarFile = new JarFile(this.task.getArchivePath())) { + assertThat(jarFile.getEntry( + "org/springframework/boot/loader/LaunchedURLClassLoader.class")) + .isNotNull(); + } + } + + @Test + public void unpackCommentIsAddedToEntryIdentifiedByAPattern() throws IOException { + this.task.setMainClass("com.example.Main"); + this.task.classpath(this.temp.newFile("one.jar"), this.temp.newFile("two.jar")); + this.task.requiresUnpack("**/one.jar"); + this.task.execute(); + try (JarFile jarFile = new JarFile(this.task.getArchivePath())) { + assertThat(jarFile.getEntry(this.libPath + "/one.jar").getComment()) + .startsWith("UNPACK:"); + assertThat(jarFile.getEntry(this.libPath + "/two.jar").getComment()).isNull(); + } + } + + @Test + public void unpackCommentIsAddedToEntryIdentifiedByASpec() throws IOException { + this.task.setMainClass("com.example.Main"); + this.task.classpath(this.temp.newFile("one.jar"), this.temp.newFile("two.jar")); + this.task.requiresUnpack((element) -> element.getName().endsWith("two.jar")); + this.task.execute(); + try (JarFile jarFile = new JarFile(this.task.getArchivePath())) { + assertThat(jarFile.getEntry(this.libPath + "/two.jar").getComment()) + .startsWith("UNPACK:"); + assertThat(jarFile.getEntry(this.libPath + "/one.jar").getComment()).isNull(); + } + } + + @Test + public void launchScriptCanBePrepended() throws IOException { + this.task.setMainClass("com.example.Main"); + this.task.getLaunchScript().setIncluded(true); + this.task.execute(); + assertThat(Files.readAllBytes(this.task.getArchivePath().toPath())) + .startsWith(new DefaultLaunchScript(null, null).toByteArray()); + } + + @Test + public void customLaunchScriptCanBePrepended() throws IOException { + this.task.setMainClass("com.example.Main"); + LaunchScriptConfiguration launchScript = this.task.getLaunchScript(); + launchScript.setIncluded(true); + File customScript = this.temp.newFile("custom.script"); + Files.write(customScript.toPath(), Arrays.asList("custom script"), + StandardOpenOption.CREATE); + launchScript.setScript(customScript); + this.task.execute(); + assertThat(Files.readAllBytes(this.task.getArchivePath().toPath())) + .startsWith("custom script".getBytes()); + } + + @Test + public void launchScriptPropertiesAreReplaced() throws IOException { + this.task.setMainClass("com.example.Main"); + LaunchScriptConfiguration launchScript = this.task.getLaunchScript(); + launchScript.setIncluded(true); + launchScript.getProperties().put("initInfoProvides", "test property value"); + this.task.execute(); + assertThat(Files.readAllBytes(this.task.getArchivePath().toPath())) + .containsSequence("test property value".getBytes()); + } + + @Test + public void customMainClassInTheManifestIsHonored() throws IOException { + this.task.setMainClass("com.example.Main"); + this.task.getManifest().getAttributes().put("Main-Class", + "com.example.CustomLauncher"); + this.task.execute(); + assertThat(this.task.getArchivePath().exists()); + try (JarFile jarFile = new JarFile(this.task.getArchivePath())) { + assertThat(jarFile.getManifest().getMainAttributes().getValue("Main-Class")) + .isEqualTo("com.example.CustomLauncher"); + assertThat(jarFile.getManifest().getMainAttributes().getValue("Start-Class")) + .isEqualTo("com.example.Main"); + } + } + + @Test + public void customStartClassInTheManifestIsHonored() throws IOException { + this.task.setMainClass("com.example.Main"); + this.task.getManifest().getAttributes().put("Start-Class", + "com.example.CustomMain"); + this.task.execute(); + assertThat(this.task.getArchivePath().exists()); + try (JarFile jarFile = new JarFile(this.task.getArchivePath())) { + assertThat(jarFile.getManifest().getMainAttributes().getValue("Main-Class")) + .isEqualTo(this.launcherClass); + assertThat(jarFile.getManifest().getMainAttributes().getValue("Start-Class")) + .isEqualTo("com.example.CustomMain"); + } + } + + private T configure(T task) throws IOException { + AbstractArchiveTask archiveTask = task; + archiveTask.setBaseName("test"); + archiveTask.setDestinationDir(this.temp.newFolder()); + return task; + } + + protected T getTask() { + return this.task; + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootJarIntegrationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootJarIntegrationTests.java new file mode 100644 index 00000000000..6a70d391a4f --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootJarIntegrationTests.java @@ -0,0 +1,30 @@ +/* + * Copyright 2012-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.boot.gradle.bundling; + +/** + * Integration tests for {@link BootJar}. + * + * @author Andy Wilkinson + */ +public class BootJarIntegrationTests extends AbstractBootArchiveIntegrationTests { + + public BootJarIntegrationTests() { + super("bootJar"); + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootJarTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootJarTests.java new file mode 100644 index 00000000000..f54198e94e6 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootJarTests.java @@ -0,0 +1,31 @@ +/* + * Copyright 2012-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.boot.gradle.bundling; + +/** + * Tests for {@link BootJar}. + * + * @author Andy Wilkinson + */ +public class BootJarTests extends AbstractBootArchiveTests { + + public BootJarTests() { + super(BootJar.class, "org.springframework.boot.loader.JarLauncher", + "BOOT-INF/lib", "BOOT-INF/classes"); + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootWarIntegrationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootWarIntegrationTests.java new file mode 100644 index 00000000000..c6fbd9c73f4 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootWarIntegrationTests.java @@ -0,0 +1,30 @@ +/* + * Copyright 2012-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.boot.gradle.bundling; + +/** + * Integration tests for {@link BootJar}. + * + * @author Andy Wilkinson + */ +public class BootWarIntegrationTests extends AbstractBootArchiveIntegrationTests { + + public BootWarIntegrationTests() { + super("bootWar"); + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootWarTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootWarTests.java new file mode 100644 index 00000000000..383d6350d15 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootWarTests.java @@ -0,0 +1,50 @@ +/* + * Copyright 2012-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.boot.gradle.bundling; + +import java.io.IOException; +import java.util.jar.JarFile; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link BootWar}. + * + * @author Andy Wilkinson + */ +public class BootWarTests extends AbstractBootArchiveTests { + + public BootWarTests() { + super(BootWar.class, "org.springframework.boot.loader.WarLauncher", "WEB-INF/lib", + "WEB-INF/classes"); + } + + @Test + public void providedClasspathJarsArePackagedInWebInfLibProvided() throws IOException { + getTask().setMainClass("com.example.Main"); + getTask().providedClasspath(this.temp.newFile("one.jar"), + this.temp.newFile("two.jar")); + getTask().execute(); + try (JarFile jarFile = new JarFile(getTask().getArchivePath())) { + assertThat(jarFile.getEntry("WEB-INF/lib-provided/one.jar")).isNotNull(); + assertThat(jarFile.getEntry("WEB-INF/lib-provided/two.jar")).isNotNull(); + } + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java new file mode 100644 index 00000000000..4bde3bea622 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java @@ -0,0 +1,113 @@ +/* + * Copyright 2012-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.boot.gradle.testkit; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import io.spring.gradle.dependencymanagement.DependencyManagementPlugin; +import org.gradle.testkit.runner.BuildResult; +import org.gradle.testkit.runner.GradleRunner; +import org.junit.rules.TemporaryFolder; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +import org.springframework.boot.loader.tools.LaunchScript; + +/** + * A {@link TestRule} for running a Gradle build using {@link GradleRunner}. + * + * @author Andy Wilkinson + */ +public class GradleBuild implements TestRule { + + private final TemporaryFolder temp = new TemporaryFolder(); + + private File projectDir; + + private String script; + + @Override + public Statement apply(Statement base, Description description) { + String name = description.getTestClass().getSimpleName() + ".gradle"; + URL scriptUrl = description.getTestClass().getResource(name); + if (scriptUrl != null) { + script(scriptUrl.getFile()); + } + return this.temp.apply(new Statement() { + + @Override + public void evaluate() throws Throwable { + before(); + try { + base.evaluate(); + } + finally { + after(); + } + } + + }, description); + } + + private void before() throws IOException { + this.projectDir = this.temp.newFolder(); + } + + private void after() { + GradleBuild.this.script = null; + } + + private String pluginClasspath() { + return new File("build/classes/main").getAbsolutePath() + "," + + new File("build/resources/main").getAbsolutePath() + "," + + LaunchScript.class.getProtectionDomain().getCodeSource().getLocation() + .getPath() + + "," + DependencyManagementPlugin.class.getProtectionDomain() + .getCodeSource().getLocation().getPath(); + } + + public GradleBuild script(String script) { + this.script = script; + return this; + } + + public BuildResult build(String... arguments) { + try { + Files.copy(new File(this.script).toPath(), + new File(this.projectDir, "build.gradle").toPath(), + StandardCopyOption.REPLACE_EXISTING); + GradleRunner gradleRunner = GradleRunner.create() + .withProjectDir(this.projectDir).forwardOutput(); + List allArguments = new ArrayList(); + allArguments.add("-PpluginClasspath=" + pluginClasspath()); + allArguments.addAll(Arrays.asList(arguments)); + return gradleRunner.withArguments(allArguments).build(); + } + catch (Exception ex) { + throw new RuntimeException(ex); + } + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/BootJarIntegrationTests.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/BootJarIntegrationTests.gradle new file mode 100644 index 00000000000..b54d7a8ecb8 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/BootJarIntegrationTests.gradle @@ -0,0 +1,16 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'java' +apply plugin: 'org.springframework.boot' + +bootJar { + mainClass = 'com.example.Application' + launchScript { + included = project.hasProperty('includeLaunchScript') ? includeLaunchScript : false + properties 'prop' : project.hasProperty('launchScriptProperty') ? launchScriptProperty : 'default' + } +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/BootWarIntegrationTests.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/BootWarIntegrationTests.gradle new file mode 100644 index 00000000000..3461c7fe001 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/BootWarIntegrationTests.gradle @@ -0,0 +1,16 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'war' +apply plugin: 'org.springframework.boot' + +bootWar { + mainClass = 'com.example.Application' + launchScript { + included = project.hasProperty('includeLaunchScript') ? includeLaunchScript : false + properties 'prop' : project.hasProperty('launchScriptProperty') ? launchScriptProperty : 'default' + } +} From b1f9123311e0579d6b4c01f375611e0c0086a5d6 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 21 Mar 2017 14:56:13 +0000 Subject: [PATCH 04/30] React to dependency management plugin rather than always applying it Previously, the Spring Boot plugin would automatically apply the dependency management plugin and import the spring-boot-dependencies bom. This made it very difficult to use Spring Boot's plugin without also using its dependency management. It also made it difficult to see where the dependency management was coming from. This commit updates the Spring Boot plugin so that it no longer automatically applies the dependency management plugin. Instead, the plugin now reacts to the dependency management plugin being applied by importing the spring-boot-dependencies bom. Users that do not wish to use Spring Boot's dependency management capabilities can now do so by not applying the dependency management plugin. Closes gh-3164 --- .../DependencyManagementPluginFeatures.java | 10 ++-- .../DependencyManagementIntegrationTests.java | 52 +++++++++++++++++++ .../boot/gradle/testkit/GradleBuild.java | 44 +++++++++++++--- ...ependencyManagementIntegrationTests.gradle | 41 +++++++++++++++ 4 files changed, 138 insertions(+), 9 deletions(-) create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/dependencymanagement/DependencyManagementIntegrationTests.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/dependencymanagement/DependencyManagementIntegrationTests.gradle diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/dependencymanagement/DependencyManagementPluginFeatures.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/dependencymanagement/DependencyManagementPluginFeatures.java index 763bad251ad..16f0c7c6f26 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/dependencymanagement/DependencyManagementPluginFeatures.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/dependencymanagement/DependencyManagementPluginFeatures.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-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. @@ -36,12 +36,16 @@ public class DependencyManagementPluginFeatures implements PluginFeatures { private static final String SPRING_BOOT_VERSION = DependencyManagementPluginFeatures.class .getPackage().getImplementationVersion(); - private static final String SPRING_BOOT_BOM = "org.springframework.boot:spring-boot-starter-parent:" + private static final String SPRING_BOOT_BOM = "org.springframework.boot:spring-boot-dependencies:" + SPRING_BOOT_VERSION; @Override public void apply(Project project) { - project.getPlugins().apply(DependencyManagementPlugin.class); + project.getPlugins().withType(DependencyManagementPlugin.class, + (plugin) -> configureDependencyManagement(project)); + } + + private void configureDependencyManagement(Project project) { DependencyManagementExtension dependencyManagement = project.getExtensions() .findByType(DependencyManagementExtension.class); dependencyManagement.imports(new Action() { diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/dependencymanagement/DependencyManagementIntegrationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/dependencymanagement/DependencyManagementIntegrationTests.java new file mode 100644 index 00000000000..f03a4dcf4b2 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/dependencymanagement/DependencyManagementIntegrationTests.java @@ -0,0 +1,52 @@ +/* + * Copyright 2012-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.boot.gradle.dependencymanagement; + +import org.gradle.testkit.runner.TaskOutcome; +import org.junit.Rule; +import org.junit.Test; + +import org.springframework.boot.gradle.testkit.GradleBuild; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for the plugin's dependency management features. + * + * @author Andy Wilkinson + */ +public class DependencyManagementIntegrationTests { + + @Rule + public GradleBuild gradleBuild = new GradleBuild(); + + @Test + public void noDependencyManagementIsAppliedByDefault() { + assertThat(this.gradleBuild.build("doesNotHaveDependencyManagement") + .task(":doesNotHaveDependencyManagement").getOutcome()) + .isEqualTo(TaskOutcome.SUCCESS); + } + + @Test + public void bomIsImportedWhenDependencyManagementPluginIsApplied() { + assertThat(this.gradleBuild + .build("hasDependencyManagement", "-PapplyDependencyManagementPlugin") + .task(":hasDependencyManagement").getOutcome()) + .isEqualTo(TaskOutcome.SUCCESS); + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java index 4bde3bea622..d3bae7f4970 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java @@ -17,6 +17,7 @@ package org.springframework.boot.gradle.testkit; import java.io.File; +import java.io.FileReader; import java.io.IOException; import java.net.URL; import java.nio.file.Files; @@ -25,6 +26,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathFactory; + import io.spring.gradle.dependencymanagement.DependencyManagementPlugin; import org.gradle.testkit.runner.BuildResult; import org.gradle.testkit.runner.GradleRunner; @@ -32,6 +37,7 @@ import org.junit.rules.TemporaryFolder; import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; +import org.xml.sax.InputSource; import org.springframework.boot.loader.tools.LaunchScript; @@ -80,12 +86,18 @@ public class GradleBuild implements TestRule { } private String pluginClasspath() { - return new File("build/classes/main").getAbsolutePath() + "," - + new File("build/resources/main").getAbsolutePath() + "," - + LaunchScript.class.getProtectionDomain().getCodeSource().getLocation() - .getPath() - + "," + DependencyManagementPlugin.class.getProtectionDomain() - .getCodeSource().getLocation().getPath(); + return absolutePath("bin") + "," + absolutePath("build/classes/main") + "," + + absolutePath("build/resources/main") + "," + + pathOfJarContaining(LaunchScript.class) + "," + + pathOfJarContaining(DependencyManagementPlugin.class); + } + + private String absolutePath(String path) { + return new File(path).getAbsolutePath(); + } + + private String pathOfJarContaining(Class type) { + return type.getProtectionDomain().getCodeSource().getLocation().getPath(); } public GradleBuild script(String script) { @@ -102,6 +114,7 @@ public class GradleBuild implements TestRule { .withProjectDir(this.projectDir).forwardOutput(); List allArguments = new ArrayList(); allArguments.add("-PpluginClasspath=" + pluginClasspath()); + allArguments.add("-PbootVersion=" + getBootVersion()); allArguments.addAll(Arrays.asList(arguments)); return gradleRunner.withArguments(allArguments).build(); } @@ -110,4 +123,23 @@ public class GradleBuild implements TestRule { } } + public static String getBootVersion() { + return evaluateExpression( + "/*[local-name()='project']/*[local-name()='parent']/*[local-name()='version']" + + "/text()"); + } + + private static String evaluateExpression(String expression) { + try { + XPathFactory xPathFactory = XPathFactory.newInstance(); + XPath xpath = xPathFactory.newXPath(); + XPathExpression expr = xpath.compile(expression); + String version = expr.evaluate(new InputSource(new FileReader("pom.xml"))); + return version; + } + catch (Exception ex) { + throw new IllegalStateException("Failed to evaluate expression", ex); + } + } + } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/dependencymanagement/DependencyManagementIntegrationTests.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/dependencymanagement/DependencyManagementIntegrationTests.gradle new file mode 100644 index 00000000000..12dca02ea44 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/dependencymanagement/DependencyManagementIntegrationTests.gradle @@ -0,0 +1,41 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'java' +apply plugin: 'org.springframework.boot' + +if (project.hasProperty('applyDependencyManagementPlugin')) { + apply plugin: 'io.spring.dependency-management' + dependencyManagement { + resolutionStrategy { + eachDependency { + if (it.requested.group == 'org.springframework.boot') { + it.useVersion project.bootVersion + } + } + } + } +} + +repositories { + mavenLocal() +} + +task doesNotHaveDependencyManagement { + doLast { + if (project.extensions.findByName('dependencyManagement') != null) { + throw new GradleException('Found dependency management extension') + } + } +} + +task hasDependencyManagement { + doLast { + if (!dependencyManagement.managedVersions) { + throw new GradleException('No managed versions have been configured') + } + } +} From f16efb2277fc76fe88ba59d1d7a5902c584535f9 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Sat, 11 Mar 2017 20:20:43 +0000 Subject: [PATCH 05/30] Publish artifacts and, when Maven plugin used, customize upload task See gh-1666 --- spring-boot-parent/pom.xml | 5 + .../bundling/BundlingPluginFeatures.java | 25 ++++ .../bundling/SinglePublishedArtifact.java | 46 ++++++++ .../bundling/MavenIntegrationTests.java | 74 ++++++++++++ .../boot/gradle/bundling/PomCondition.java | 111 ++++++++++++++++++ .../boot/gradle/testkit/GradleBuild.java | 31 ++++- ...tegrationTests-bootJarCanBeUploaded.gradle | 24 ++++ ...tegrationTests-bootWarCanBeUploaded.gradle | 24 ++++ 8 files changed, 337 insertions(+), 3 deletions(-) create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/SinglePublishedArtifact.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/MavenIntegrationTests.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/PomCondition.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/MavenIntegrationTests-bootJarCanBeUploaded.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/MavenIntegrationTests-bootWarCanBeUploaded.gradle diff --git a/spring-boot-parent/pom.xml b/spring-boot-parent/pom.xml index 4c223f882bd..4949c3ba7eb 100644 --- a/spring-boot-parent/pom.xml +++ b/spring-boot-parent/pom.xml @@ -224,6 +224,11 @@ gradle-language-jvm ${gradle.version} + + org.gradle + gradle-maven + ${gradle.version} + org.gradle gradle-platform-jvm diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BundlingPluginFeatures.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BundlingPluginFeatures.java index f49ff1bf90c..2e42d7d1be3 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BundlingPluginFeatures.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BundlingPluginFeatures.java @@ -20,11 +20,14 @@ import java.util.concurrent.Callable; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.maven.MavenResolver; import org.gradle.api.file.FileCollection; +import org.gradle.api.internal.artifacts.publish.ArchivePublishArtifact; import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPluginConvention; import org.gradle.api.plugins.WarPlugin; import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.Upload; import org.springframework.boot.gradle.PluginFeatures; @@ -35,17 +38,23 @@ import org.springframework.boot.gradle.PluginFeatures; */ public class BundlingPluginFeatures implements PluginFeatures { + private SinglePublishedArtifact singlePublishedArtifact; + @Override public void apply(Project project) { + this.singlePublishedArtifact = new SinglePublishedArtifact( + project.getConfigurations().create("bootArchives").getArtifacts()); project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> configureBootJarTask(project)); project.getPlugins().withType(WarPlugin.class, (warPlugin) -> configureBootWarTask(project)); + project.afterEvaluate(this::configureBootArchivesUpload); } private void configureBootWarTask(Project project) { BootWar bootWar = project.getTasks().create("bootWar", BootWar.class); bootWar.providedClasspath(providedRuntimeConfiguration(project)); + this.singlePublishedArtifact.addCandidate(new ArchivePublishArtifact(bootWar)); } private void configureBootJarTask(Project project) { @@ -57,6 +66,22 @@ public class BundlingPluginFeatures implements PluginFeatures { .getByName(SourceSet.MAIN_SOURCE_SET_NAME); return mainSourceSet.getRuntimeClasspath(); }); + this.singlePublishedArtifact.addCandidate(new ArchivePublishArtifact(bootJar)); + } + + private void configureBootArchivesUpload(Project project) { + Upload upload = project.getTasks().withType(Upload.class) + .findByName("uploadBootArchives"); + if (upload == null) { + return; + } + clearConfigurationMappings(upload); + } + + private void clearConfigurationMappings(Upload upload) { + upload.getRepositories().withType(MavenResolver.class, (resolver) -> { + resolver.getPom().getScopeMappings().getMappings().clear(); + }); } private Configuration providedRuntimeConfiguration(Project project) { diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/SinglePublishedArtifact.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/SinglePublishedArtifact.java new file mode 100644 index 00000000000..eeafb064c3c --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/SinglePublishedArtifact.java @@ -0,0 +1,46 @@ +/* + * Copyright 2012-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.boot.gradle.bundling; + +import org.gradle.api.artifacts.PublishArtifact; +import org.gradle.api.artifacts.PublishArtifactSet; + +/** + * A wrapper for a {@PublishArtifactSet} that ensures that only a single artifact is + * published, with a war file taking precedence over a jar file. + * + * @author Andy Wilkinson + */ +class SinglePublishedArtifact { + + private final PublishArtifactSet artifacts; + + private PublishArtifact currentArtifact; + + SinglePublishedArtifact(PublishArtifactSet artifacts) { + this.artifacts = artifacts; + } + + void addCandidate(PublishArtifact candidate) { + if (this.currentArtifact == null || "war".equals(candidate.getExtension())) { + this.artifacts.clear(); + this.artifacts.add(candidate); + this.currentArtifact = candidate; + } + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/MavenIntegrationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/MavenIntegrationTests.java new file mode 100644 index 00000000000..1954cc81700 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/MavenIntegrationTests.java @@ -0,0 +1,74 @@ +/* + * Copyright 2012-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.boot.gradle.bundling; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; + +import org.gradle.testkit.runner.BuildResult; +import org.gradle.testkit.runner.TaskOutcome; +import org.junit.Rule; +import org.junit.Test; + +import org.springframework.boot.gradle.testkit.GradleBuild; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for uploading Boot jars and wars using Gradle's Maven plugin. + * + * @author Andy Wilkinson + */ +public class MavenIntegrationTests { + + @Rule + public final GradleBuild gradleBuild = new GradleBuild(); + + @Test + public void bootJarCanBeUploaded() throws FileNotFoundException, IOException { + BuildResult result = this.gradleBuild.build("uploadBootArchives"); + assertThat(result.task(":uploadBootArchives").getOutcome()) + .isEqualTo(TaskOutcome.SUCCESS); + assertThat(artifactWithSuffix("jar")).isFile(); + assertThat(artifactWithSuffix("pom")).is(pomWith().groupId("com.example") + .artifactId(this.gradleBuild.getProjectDir().getName()).version("1.0") + .noPackaging().noDependencies()); + } + + @Test + public void bootWarCanBeUploaded() throws IOException { + BuildResult result = this.gradleBuild.build("uploadBootArchives"); + assertThat(result.task(":uploadBootArchives").getOutcome()) + .isEqualTo(TaskOutcome.SUCCESS); + assertThat(artifactWithSuffix("war")).isFile(); + assertThat(artifactWithSuffix("pom")).is(pomWith().groupId("com.example") + .artifactId(this.gradleBuild.getProjectDir().getName()).version("1.0") + .packaging("war").noDependencies()); + } + + private File artifactWithSuffix(String suffix) { + String name = this.gradleBuild.getProjectDir().getName(); + return new File(new File(this.gradleBuild.getProjectDir(), "build/repo"), + String.format("com/example/%s/1.0/%s-1.0.%s", name, name, suffix)); + } + + private PomCondition pomWith() { + return new PomCondition(); + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/PomCondition.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/PomCondition.java new file mode 100644 index 00000000000..f05fff957ee --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/PomCondition.java @@ -0,0 +1,111 @@ +/* + * Copyright 2012-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.boot.gradle.bundling; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +import org.assertj.core.api.Condition; +import org.assertj.core.description.Description; +import org.assertj.core.description.TextDescription; + +import org.springframework.util.FileCopyUtils; + +/** + * AssertJ {@link Condition} for asserting the contents of a pom file. + * + * @author Andy Wilkinson + */ +class PomCondition extends Condition { + + private Set expectedContents; + + private Set notExpectedContents; + + PomCondition() { + this(new HashSet(), new HashSet()); + } + + private PomCondition(Set expectedContents, Set notExpectedContents) { + super(new TextDescription("Pom file containing %s and not containing %s", + expectedContents, notExpectedContents)); + this.expectedContents = expectedContents; + this.notExpectedContents = notExpectedContents; + } + + @Override + public boolean matches(File pom) { + try { + String contents = FileCopyUtils.copyToString(new FileReader(pom)); + for (String expected : this.expectedContents) { + if (!contents.contains(expected)) { + return false; + } + } + for (String notExpected : this.notExpectedContents) { + if (contents.contains(notExpected)) { + return false; + } + } + } + catch (IOException ex) { + throw new RuntimeException(ex); + } + return true; + } + + @Override + public Description description() { + return new TextDescription("Pom file containing %s and not containing %s", + this.expectedContents, this.notExpectedContents); + } + + PomCondition groupId(String groupId) { + this.expectedContents.add(String.format("%s", groupId)); + return this; + } + + PomCondition artifactId(String artifactId) { + this.expectedContents + .add(String.format("%s", artifactId)); + return this; + } + + PomCondition version(String version) { + this.expectedContents.add(String.format("%s", version)); + return this; + } + + PomCondition packaging(String packaging) { + this.expectedContents.add(String.format("%s", packaging)); + return this; + } + + PomCondition noDependencies() { + this.notExpectedContents.add(""); + return this; + } + + PomCondition noPackaging() { + this.notExpectedContents.add(""); + return this; + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java index d3bae7f4970..078446016d4 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java @@ -56,8 +56,7 @@ public class GradleBuild implements TestRule { @Override public Statement apply(Statement base, Description description) { - String name = description.getTestClass().getSimpleName() + ".gradle"; - URL scriptUrl = description.getTestClass().getResource(name); + URL scriptUrl = findDefaultScript(description); if (scriptUrl != null) { script(scriptUrl.getFile()); } @@ -77,6 +76,24 @@ public class GradleBuild implements TestRule { }, description); } + private URL findDefaultScript(Description description) { + URL scriptUrl = getScriptForTestMethod(description); + if (scriptUrl != null) { + return scriptUrl; + } + return getScriptForTestClass(description.getTestClass()); + } + + private URL getScriptForTestMethod(Description description) { + return description.getTestClass() + .getResource(description.getTestClass().getSimpleName() + "-" + + description.getMethodName() + ".gradle"); + } + + private URL getScriptForTestClass(Class testClass) { + return testClass.getResource(testClass.getSimpleName() + ".gradle"); + } + private void before() throws IOException { this.projectDir = this.temp.newFolder(); } @@ -123,7 +140,15 @@ public class GradleBuild implements TestRule { } } - public static String getBootVersion() { + public File getProjectDir() { + return this.projectDir; + } + + public void setProjectDir(File projectDir) { + this.projectDir = projectDir; + } + + private static String getBootVersion() { return evaluateExpression( "/*[local-name()='project']/*[local-name()='parent']/*[local-name()='version']" + "/text()"); diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/MavenIntegrationTests-bootJarCanBeUploaded.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/MavenIntegrationTests-bootJarCanBeUploaded.gradle new file mode 100644 index 00000000000..145bc8ac02a --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/MavenIntegrationTests-bootJarCanBeUploaded.gradle @@ -0,0 +1,24 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'java' +apply plugin: 'org.springframework.boot' +apply plugin: 'maven' + +bootJar { + mainClass = 'com.example.Application' +} + +group = 'com.example' +version = '1.0' + +uploadBootArchives { + repositories { + mavenDeployer { + repository(url: "file:$buildDir/repo") + } + } +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/MavenIntegrationTests-bootWarCanBeUploaded.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/MavenIntegrationTests-bootWarCanBeUploaded.gradle new file mode 100644 index 00000000000..89c8e25f60d --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/MavenIntegrationTests-bootWarCanBeUploaded.gradle @@ -0,0 +1,24 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'war' +apply plugin: 'org.springframework.boot' +apply plugin: 'maven' + +bootWar { + mainClass = 'com.example.Application' +} + +group = 'com.example' +version = '1.0' + +uploadBootArchives { + repositories { + mavenDeployer { + repository(url: "file:$buildDir/repo") + } + } +} From b4e2044b9e8539478c5dce9197f1e35c8f64d236 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Sat, 11 Mar 2017 21:06:12 +0000 Subject: [PATCH 06/30] Simplify bootRun main class configuration by reusing MainClassSupplier --- .../{bundling => }/MainClassSupplier.java | 42 +++-- .../boot/gradle/bundling/BootJar.java | 2 + .../boot/gradle/bundling/BootWar.java | 2 + .../boot/gradle/run/BootRunTask.java | 16 ++ .../boot/gradle/run/FindMainClassTask.java | 143 ------------------ .../boot/gradle/run/RunPluginFeatures.java | 41 ----- 6 files changed, 51 insertions(+), 195 deletions(-) rename spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/{bundling => }/MainClassSupplier.java (55%) delete mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/FindMainClassTask.java diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/MainClassSupplier.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/MainClassSupplier.java similarity index 55% rename from spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/MainClassSupplier.java rename to spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/MainClassSupplier.java index 1e6a76b4eb7..0b493f1a672 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/MainClassSupplier.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/MainClassSupplier.java @@ -14,10 +14,11 @@ * limitations under the License. */ -package org.springframework.boot.gradle.bundling; +package org.springframework.boot.gradle; import java.io.File; import java.io.IOException; +import java.util.Objects; import java.util.function.Supplier; import org.gradle.api.file.FileCollection; @@ -31,41 +32,60 @@ import org.springframework.boot.loader.tools.MainClassFinder; * * @author Andy Wilkinson */ -class MainClassSupplier implements Supplier { +public class MainClassSupplier implements Supplier { + + private static final String SPRING_BOOT_APPLICATION_CLASS_NAME = "org.springframework.boot.autoconfigure.SpringBootApplication"; private final Supplier classpathSupplier; private String mainClass; - MainClassSupplier(Supplier classpathSupplier) { + /** + * Creates a new {@code MainClassSupplier} that will fall back to searching + * directories in the classpath supplied by the given {@code classpathSupplier} for + * the application's main class. + * + * @param classpathSupplier the supplier of the classpath + */ + public MainClassSupplier(Supplier classpathSupplier) { this.classpathSupplier = classpathSupplier; } @Override public String get() { - if (this.mainClass != null) { - return this.mainClass; + if (this.mainClass == null) { + this.mainClass = findMainClass(); } - return findMainClass(); + return this.mainClass; } private String findMainClass() { FileCollection classpath = this.classpathSupplier.get(); - return classpath == null ? null - : classpath.filter(File::isDirectory).getFiles().stream() - .map(this::findMainClass).findFirst().orElse(null); + if (classpath == null) { + return null; + } + return classpath.filter(File::isDirectory).getFiles().stream() + .map(this::findMainClass).filter(Objects::nonNull).findFirst() + .orElse(null); } private String findMainClass(File file) { try { - return MainClassFinder.findSingleMainClass(file); + String result = MainClassFinder.findSingleMainClass(file, + SPRING_BOOT_APPLICATION_CLASS_NAME); + return result; } catch (IOException ex) { return null; } } - void setMainClass(String mainClass) { + /** + * Sets the {@code mainClass} that will be supplied. + * + * @param mainClass the main class to supply + */ + public void setMainClass(String mainClass) { this.mainClass = mainClass; } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootJar.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootJar.java index 3a118c8e6e8..03aaeaaaeb7 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootJar.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootJar.java @@ -28,6 +28,8 @@ import org.gradle.api.internal.file.copy.CopyAction; import org.gradle.api.specs.Spec; import org.gradle.api.tasks.bundling.Jar; +import org.springframework.boot.gradle.MainClassSupplier; + /** * A custom {@link Jar} task that produces a Spring Boot executable jar. * diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootWar.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootWar.java index dddebe0777a..33f425ed19f 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootWar.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootWar.java @@ -29,6 +29,8 @@ import org.gradle.api.specs.Spec; import org.gradle.api.tasks.Optional; import org.gradle.api.tasks.bundling.War; +import org.springframework.boot.gradle.MainClassSupplier; + /** * A custom {@link War} task that produces a Spring Boot executable war. * diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/BootRunTask.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/BootRunTask.java index 7f51972f9f1..9f40aacdb9c 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/BootRunTask.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/BootRunTask.java @@ -26,6 +26,7 @@ import org.gradle.api.internal.file.collections.SimpleFileCollection; import org.gradle.api.tasks.JavaExec; import org.gradle.api.tasks.SourceSet; +import org.springframework.boot.gradle.MainClassSupplier; import org.springframework.boot.loader.tools.FileUtils; /** @@ -33,9 +34,13 @@ import org.springframework.boot.loader.tools.FileUtils; * * @author Dave Syer * @author Phillip Webb + * @author Andy Wilkinson */ public class BootRunTask extends JavaExec { + private final MainClassSupplier mainClassSupplier = new MainClassSupplier( + this::getClasspath); + /** * Whether or not resources (typically in {@code src/main/resources} are added * directly to the classpath. When enabled, this allows live in-place editing of @@ -62,6 +67,17 @@ public class BootRunTask extends JavaExec { super.exec(); } + @Override + public String getMain() { + return this.mainClassSupplier.get(); + } + + @Override + public JavaExec setMain(String mainClassName) { + this.mainClassSupplier.setMainClass(mainClassName); + return super.setMain(mainClassName); + } + private void addResourcesIfNecessary() { if (this.addResources) { SourceSet mainSourceSet = SourceSets.findMainSourceSet(getProject()); diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/FindMainClassTask.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/FindMainClassTask.java deleted file mode 100644 index c3d2470a92b..00000000000 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/FindMainClassTask.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright 2012-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.boot.gradle.run; - -import java.io.IOException; - -import org.gradle.api.DefaultTask; -import org.gradle.api.Project; -import org.gradle.api.Task; -import org.gradle.api.plugins.ApplicationPluginConvention; -import org.gradle.api.plugins.ExtraPropertiesExtension; -import org.gradle.api.tasks.Input; -import org.gradle.api.tasks.JavaExec; -import org.gradle.api.tasks.SourceSetOutput; -import org.gradle.api.tasks.TaskAction; - -import org.springframework.boot.gradle.SpringBootPluginExtension; -import org.springframework.boot.loader.tools.MainClassFinder; - -/** - * Task to find and set the 'mainClassName' convention when it's missing by searching the - * main source code. - * - * @author Dave Syer - * @author Phillip Webb - * @author Andy Wilkinson - */ -public class FindMainClassTask extends DefaultTask { - - private static final String SPRING_BOOT_APPLICATION_CLASS_NAME = "org.springframework.boot.autoconfigure.SpringBootApplication"; - - @Input - private SourceSetOutput mainClassSourceSetOutput; - - public void setMainClassSourceSetOutput(SourceSetOutput sourceSetOutput) { - this.mainClassSourceSetOutput = sourceSetOutput; - this.dependsOn(this.mainClassSourceSetOutput.getBuildDependencies()); - } - - @TaskAction - public void setMainClassNameProperty() { - Project project = getProject(); - if (!project.hasProperty("mainClassName") - || project.property("mainClassName") == null) { - String mainClass = findMainClass(); - if (project.hasProperty("mainClassName")) { - project.setProperty("mainClassName", mainClass); - } - else { - ExtraPropertiesExtension extraProperties = (ExtraPropertiesExtension) project - .getExtensions().getByName("ext"); - extraProperties.set("mainClassName", mainClass); - } - } - } - - private String findMainClass() { - Project project = getProject(); - - String mainClass = null; - - // Try the SpringBoot extension setting - SpringBootPluginExtension bootExtension = project.getExtensions() - .getByType(SpringBootPluginExtension.class); - if (bootExtension.getMainClass() != null) { - mainClass = bootExtension.getMainClass(); - } - - ApplicationPluginConvention application = (ApplicationPluginConvention) project - .getConvention().getPlugins().get("application"); - - if (mainClass == null && application != null) { - // Try the Application extension setting - mainClass = application.getMainClassName(); - } - - JavaExec runTask = findRunTask(project); - if (mainClass == null && runTask != null) { - mainClass = runTask.getMain(); - } - - if (mainClass == null) { - Task bootRunTask = project.getTasks().findByName("bootRun"); - if (bootRunTask != null) { - mainClass = (String) bootRunTask.property("main"); - } - } - - if (mainClass == null) { - // Search - if (this.mainClassSourceSetOutput != null) { - project.getLogger().debug("Looking for main in: " - + this.mainClassSourceSetOutput.getClassesDir()); - try { - mainClass = MainClassFinder.findSingleMainClass( - this.mainClassSourceSetOutput.getClassesDir(), - SPRING_BOOT_APPLICATION_CLASS_NAME); - project.getLogger().info("Computed main class: " + mainClass); - } - catch (IOException ex) { - throw new IllegalStateException("Cannot find main class", ex); - } - } - } - - project.getLogger().info("Found main: " + mainClass); - - if (bootExtension.getMainClass() == null) { - bootExtension.setMainClass(mainClass); - } - if (application != null && application.getMainClassName() == null) { - application.setMainClassName(mainClass); - } - if (runTask != null && !runTask.hasProperty("main")) { - runTask.setMain(mainClass); - } - - return mainClass; - } - - private JavaExec findRunTask(Project project) { - Task runTask = project.getTasks().findByName("run"); - if (runTask instanceof JavaExec) { - return (JavaExec) runTask; - } - return null; - } - -} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/RunPluginFeatures.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/RunPluginFeatures.java index 4ac2b1e507a..41b335b83ea 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/RunPluginFeatures.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/RunPluginFeatures.java @@ -19,14 +19,9 @@ package org.springframework.boot.gradle.run; import java.util.Collections; import java.util.concurrent.Callable; -import org.gradle.api.Action; import org.gradle.api.Project; -import org.gradle.api.Task; -import org.gradle.api.plugins.ExtraPropertiesExtension; import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPluginConvention; -import org.gradle.api.tasks.SourceSet; -import org.gradle.api.tasks.application.CreateStartScripts; import org.springframework.boot.gradle.PluginFeatures; @@ -37,35 +32,15 @@ import org.springframework.boot.gradle.PluginFeatures; */ public class RunPluginFeatures implements PluginFeatures { - private static final String FIND_MAIN_CLASS_TASK_NAME = "findMainClass"; - private static final String RUN_APP_TASK_NAME = "bootRun"; @Override public void apply(Project project) { project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> { - mainClassNameFinder(project); addBootRunTask(project); }); } - private void mainClassNameFinder(Project project) { - FindMainClassTask findMainClassTask = project.getTasks() - .create(FIND_MAIN_CLASS_TASK_NAME, FindMainClassTask.class); - SourceSet mainSourceSet = SourceSets.findMainSourceSet(project); - if (mainSourceSet != null) { - findMainClassTask.setMainClassSourceSetOutput(mainSourceSet.getOutput()); - } - project.getTasks().all(new Action() { - @Override - public void execute(Task task) { - if (task instanceof BootRunTask || task instanceof CreateStartScripts) { - task.dependsOn(FIND_MAIN_CLASS_TASK_NAME); - } - } - }); - } - private void addBootRunTask(final Project project) { final JavaPluginConvention javaConvention = project.getConvention() .getPlugin(JavaPluginConvention.class); @@ -76,22 +51,6 @@ public class RunPluginFeatures implements PluginFeatures { run.setGroup("application"); run.setClasspath( javaConvention.getSourceSets().findByName("main").getRuntimeClasspath()); - run.getConventionMapping().map("main", new Callable() { - @Override - public Object call() throws Exception { - if (project.hasProperty("mainClassName") - && project.property("mainClassName") != null) { - return project.property("mainClassName"); - } - ExtraPropertiesExtension extraPropertiesExtension = (ExtraPropertiesExtension) project - .getExtensions().getByName("ext"); - if (extraPropertiesExtension.has("mainClassName") - && extraPropertiesExtension.get("mainClassName") != null) { - return extraPropertiesExtension.get("mainClassName"); - } - return null; - } - }); run.getConventionMapping().map("jvmArgs", new Callable() { @Override public Object call() throws Exception { From 31febfa383df0da4f4c1afc7f9f093b14da9b0b7 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 13 Mar 2017 07:24:08 +0000 Subject: [PATCH 07/30] Create distribution for Boot jar or war when application plugin applied Closes gh-2622 --- .../ApplicationPluginFeatures.java | 107 +++++++++++ .../application/CreateBootStartScripts.java | 41 +++++ .../boot/gradle/plugin/SpringBootPlugin.java | 2 + .../src/main/resources/unixStartScript.txt | 172 ++++++++++++++++++ .../src/main/resources/windowsStartScript.txt | 85 +++++++++ 5 files changed, 407 insertions(+) create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/application/ApplicationPluginFeatures.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/application/CreateBootStartScripts.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/resources/unixStartScript.txt create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/resources/windowsStartScript.txt diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/application/ApplicationPluginFeatures.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/application/ApplicationPluginFeatures.java new file mode 100644 index 00000000000..2eadf41ab6c --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/application/ApplicationPluginFeatures.java @@ -0,0 +1,107 @@ +/* + * Copyright 2012-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.boot.gradle.application; + +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.StringWriter; +import java.util.concurrent.Callable; + +import org.gradle.api.GradleException; +import org.gradle.api.Project; +import org.gradle.api.distribution.Distribution; +import org.gradle.api.distribution.DistributionContainer; +import org.gradle.api.file.CopySpec; +import org.gradle.api.file.FileCollection; +import org.gradle.api.plugins.ApplicationPlugin; +import org.gradle.api.plugins.ApplicationPluginConvention; +import org.gradle.jvm.application.scripts.TemplateBasedScriptGenerator; + +import org.springframework.boot.gradle.PluginFeatures; + +/** + * Features that are configured when the application plugin is applied. + * + * @author Andy Wilkinson + */ +public class ApplicationPluginFeatures implements PluginFeatures { + + @Override + public void apply(Project project) { + project.getPlugins().withType(ApplicationPlugin.class, + (plugin) -> configureDistribution(project)); + } + + public void configureDistribution(Project project) { + ApplicationPluginConvention applicationConvention = project.getConvention() + .getPlugin(ApplicationPluginConvention.class); + DistributionContainer distributions = project.getExtensions() + .getByType(DistributionContainer.class); + Distribution distribution = distributions.create("boot"); + CreateBootStartScripts bootStartScripts = project.getTasks() + .create("bootStartScripts", CreateBootStartScripts.class); + ((TemplateBasedScriptGenerator) bootStartScripts.getUnixStartScriptGenerator()) + .setTemplate(project.getResources().getText() + .fromString(loadResource("/unixStartScript.txt"))); + ((TemplateBasedScriptGenerator) bootStartScripts.getWindowsStartScriptGenerator()) + .setTemplate(project.getResources().getText() + .fromString(loadResource("/windowsStartScript.txt"))); + project.getConfigurations().all((configuration) -> { + if ("bootArchives".equals(configuration.getName())) { + distribution.getContents().with(project.copySpec().into("lib") + .from((Callable) () -> { + return configuration.getArtifacts().getFiles(); + })); + bootStartScripts.setClasspath(configuration.getArtifacts().getFiles()); + } + }); + bootStartScripts.getConventionMapping().map("outputDir", + () -> new File(project.getBuildDir(), "bootScripts")); + bootStartScripts.getConventionMapping().map("applicationName", + () -> applicationConvention.getApplicationName()); + CopySpec binCopySpec = project.copySpec().into("bin").from(bootStartScripts); + binCopySpec.setFileMode(0755); + distribution.getContents().with(binCopySpec); + } + + private String loadResource(String name) { + InputStreamReader reader = new InputStreamReader( + getClass().getResourceAsStream(name)); + char[] buffer = new char[4096]; + int read = 0; + StringWriter writer = new StringWriter(); + try { + while ((read = reader.read(buffer)) > 0) { + writer.write(buffer, 0, read); + } + return writer.toString(); + } + catch (IOException ex) { + throw new GradleException("Failed to read '" + name + "'", ex); + } + finally { + try { + reader.close(); + } + catch (IOException ex) { + // Continue + } + } + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/application/CreateBootStartScripts.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/application/CreateBootStartScripts.java new file mode 100644 index 00000000000..9f11a5d51ef --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/application/CreateBootStartScripts.java @@ -0,0 +1,41 @@ +/* + * Copyright 2012-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.boot.gradle.application; + +import org.gradle.api.tasks.Optional; +import org.gradle.jvm.application.tasks.CreateStartScripts; + +/** + * Customization of {@link CreateStartScripts} that makes the {@link #getMainClassName() + * main class name} optional. + * + * @author Andy Wilkinson + */ +public class CreateBootStartScripts extends CreateStartScripts { + + @Override + @Optional + public String getMainClassName() { + return super.getMainClassName(); + } + + @Override + public void setMainClassName(String mainClassName) { + super.setMainClassName(mainClassName); + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java index 0879b8c3d11..7b1efcb8f49 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java @@ -24,6 +24,7 @@ import org.gradle.api.tasks.compile.JavaCompile; import org.springframework.boot.gradle.SpringBootPluginExtension; import org.springframework.boot.gradle.agent.AgentPluginFeatures; +import org.springframework.boot.gradle.application.ApplicationPluginFeatures; import org.springframework.boot.gradle.bundling.BundlingPluginFeatures; import org.springframework.boot.gradle.dependencymanagement.DependencyManagementPluginFeatures; import org.springframework.boot.gradle.run.RunPluginFeatures; @@ -42,6 +43,7 @@ public class SpringBootPlugin implements Plugin { project.getExtensions().create("springBoot", SpringBootPluginExtension.class, project); new AgentPluginFeatures().apply(project); + new ApplicationPluginFeatures().apply(project); new BundlingPluginFeatures().apply(project); new RunPluginFeatures().apply(project); new DependencyManagementPluginFeatures().apply(project); diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/resources/unixStartScript.txt b/spring-boot-tools/spring-boot-gradle-plugin/src/main/resources/unixStartScript.txt new file mode 100644 index 00000000000..7c3539c7c2a --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/resources/unixStartScript.txt @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## ${applicationName} 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\"`/${appHomeRelativePath}" >/dev/null +APP_HOME="`pwd -P`" +cd "\$SAVED" >/dev/null + +APP_NAME="${applicationName}" +APP_BASE_NAME=`basename "\$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and ${optsEnvironmentVar} to pass JVM options to this script. +DEFAULT_JVM_OPTS=${defaultJvmOpts} + +# 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 + +JARPATH=$classpath + +# 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"` + JARPATH=`cygpath --path --mixed "\$JARPATH"` + 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 \$${optsEnvironmentVar} <% if ( appNameSystemProperty ) { %>"\"-D${appNameSystemProperty}=\$APP_BASE_NAME\"" <% } %>-jar "\"\$JARPATH\"" "\$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/spring-boot-tools/spring-boot-gradle-plugin/src/main/resources/windowsStartScript.txt b/spring-boot-tools/spring-boot-gradle-plugin/src/main/resources/windowsStartScript.txt new file mode 100644 index 00000000000..58f2a3d59cf --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/resources/windowsStartScript.txt @@ -0,0 +1,85 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem ${applicationName} 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%${appHomeRelativePath} + +@rem Add default JVM options here. You can also use JAVA_OPTS and ${optsEnvironmentVar} to pass JVM options to this script. +set DEFAULT_JVM_OPTS=${defaultJvmOpts} + +@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 JARPATH=$classpath + +@rem Execute ${applicationName} +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %${optsEnvironmentVar}% <% if ( appNameSystemProperty ) { %>"-D${appNameSystemProperty}=%APP_BASE_NAME%"<% } %> -jar "%JARPATH%" %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable ${exitEnvironmentVar} if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%${exitEnvironmentVar}%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega From d9af21ab7c7fffe9750ab263670e2269f82b6f10 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 16 Mar 2017 15:47:30 +0000 Subject: [PATCH 08/30] Create software components for Spring Boot jar and war artifacts Closes gh-1666 --- .../bundling/BundlingPluginFeatures.java | 70 +++++++++++++++++- .../MavenPublishingIntegrationTests.java | 73 +++++++++++++++++++ ...egrationTests-bootJarCanBePublished.gradle | 29 ++++++++ ...egrationTests-bootWarCanBePublished.gradle | 29 ++++++++ 4 files changed, 199 insertions(+), 2 deletions(-) create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/MavenPublishingIntegrationTests.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/MavenPublishingIntegrationTests-bootJarCanBePublished.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/MavenPublishingIntegrationTests-bootWarCanBePublished.gradle diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BundlingPluginFeatures.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BundlingPluginFeatures.java index 2e42d7d1be3..92135c6ff8e 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BundlingPluginFeatures.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BundlingPluginFeatures.java @@ -16,13 +16,21 @@ package org.springframework.boot.gradle.bundling; +import java.util.Collections; +import java.util.Set; import java.util.concurrent.Callable; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.ModuleDependency; +import org.gradle.api.artifacts.PublishArtifact; import org.gradle.api.artifacts.maven.MavenResolver; +import org.gradle.api.attributes.Usage; import org.gradle.api.file.FileCollection; import org.gradle.api.internal.artifacts.publish.ArchivePublishArtifact; +import org.gradle.api.internal.attributes.Usages; +import org.gradle.api.internal.component.SoftwareComponentInternal; +import org.gradle.api.internal.component.UsageContext; import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPluginConvention; import org.gradle.api.plugins.WarPlugin; @@ -54,7 +62,9 @@ public class BundlingPluginFeatures implements PluginFeatures { private void configureBootWarTask(Project project) { BootWar bootWar = project.getTasks().create("bootWar", BootWar.class); bootWar.providedClasspath(providedRuntimeConfiguration(project)); - this.singlePublishedArtifact.addCandidate(new ArchivePublishArtifact(bootWar)); + ArchivePublishArtifact artifact = new ArchivePublishArtifact(bootWar); + this.singlePublishedArtifact.addCandidate(artifact); + project.getComponents().add(new BootSoftwareComponent(artifact, "bootWeb")); } private void configureBootJarTask(Project project) { @@ -66,7 +76,9 @@ public class BundlingPluginFeatures implements PluginFeatures { .getByName(SourceSet.MAIN_SOURCE_SET_NAME); return mainSourceSet.getRuntimeClasspath(); }); - this.singlePublishedArtifact.addCandidate(new ArchivePublishArtifact(bootJar)); + ArchivePublishArtifact artifact = new ArchivePublishArtifact(bootJar); + this.singlePublishedArtifact.addCandidate(artifact); + project.getComponents().add(new BootSoftwareComponent(artifact, "bootJava")); } private void configureBootArchivesUpload(Project project) { @@ -89,4 +101,58 @@ public class BundlingPluginFeatures implements PluginFeatures { .getByName(WarPlugin.PROVIDED_RUNTIME_CONFIGURATION_NAME); } + /** + * {@link SofwareComponent} for a Spring Boot fat jar or war. + */ + private static final class BootSoftwareComponent + implements SoftwareComponentInternal { + + private final PublishArtifact artifact; + + private final String name; + + private BootSoftwareComponent(PublishArtifact artifact, String name) { + this.artifact = artifact; + this.name = name; + } + + @Override + public String getName() { + return this.name; + } + + @Override + public Set getUsages() { + return Collections.singleton(new BootUsageContext(this.artifact)); + } + + private static final class BootUsageContext implements UsageContext { + + private static final Usage USAGE = Usages.usage("master"); + + private final PublishArtifact artifact; + + private BootUsageContext(PublishArtifact artifact) { + this.artifact = artifact; + } + + @Override + public Usage getUsage() { + return USAGE; + } + + @Override + public Set getArtifacts() { + return Collections.singleton(this.artifact); + } + + @Override + public Set getDependencies() { + return Collections.emptySet(); + } + + } + + } + } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/MavenPublishingIntegrationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/MavenPublishingIntegrationTests.java new file mode 100644 index 00000000000..2f4ad97a9b5 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/MavenPublishingIntegrationTests.java @@ -0,0 +1,73 @@ +/* + * Copyright 2012-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.boot.gradle.bundling; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; + +import org.gradle.testkit.runner.BuildResult; +import org.gradle.testkit.runner.TaskOutcome; +import org.junit.Rule; +import org.junit.Test; + +import org.springframework.boot.gradle.testkit.GradleBuild; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for publishing Boot jars and wars using Gradle's Maven Publish + * plugin. + * + * @author Andy Wilkinson + */ +public class MavenPublishingIntegrationTests { + + @Rule + public final GradleBuild gradleBuild = new GradleBuild(); + + @Test + public void bootJarCanBePublished() throws FileNotFoundException, IOException { + BuildResult result = this.gradleBuild.build("publish"); + assertThat(result.task(":publish").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + assertThat(artifactWithSuffix("jar")).isFile(); + assertThat(artifactWithSuffix("pom")).is(pomWith().groupId("com.example") + .artifactId(this.gradleBuild.getProjectDir().getName()).version("1.0") + .noPackaging().noDependencies()); + } + + @Test + public void bootWarCanBePublished() throws IOException { + BuildResult result = this.gradleBuild.build("publish"); + assertThat(result.task(":publish").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + assertThat(artifactWithSuffix("war")).isFile(); + assertThat(artifactWithSuffix("pom")).is(pomWith().groupId("com.example") + .artifactId(this.gradleBuild.getProjectDir().getName()).version("1.0") + .packaging("war").noDependencies()); + } + + private File artifactWithSuffix(String suffix) { + String name = this.gradleBuild.getProjectDir().getName(); + return new File(new File(this.gradleBuild.getProjectDir(), "build/repo"), + String.format("com/example/%s/1.0/%s-1.0.%s", name, name, suffix)); + } + + private PomCondition pomWith() { + return new PomCondition(); + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/MavenPublishingIntegrationTests-bootJarCanBePublished.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/MavenPublishingIntegrationTests-bootJarCanBePublished.gradle new file mode 100644 index 00000000000..fbc1cb0dba9 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/MavenPublishingIntegrationTests-bootJarCanBePublished.gradle @@ -0,0 +1,29 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'java' +apply plugin: 'org.springframework.boot' +apply plugin: 'maven-publish' + +bootJar { + mainClass = 'com.example.Application' +} + +group = 'com.example' +version = '1.0' + +publishing { + repositories { + maven { + url "$buildDir/repo" + } + } + publications { + mavenBootJava(MavenPublication) { + from components.bootJava + } + } +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/MavenPublishingIntegrationTests-bootWarCanBePublished.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/MavenPublishingIntegrationTests-bootWarCanBePublished.gradle new file mode 100644 index 00000000000..4c71589e0c2 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/MavenPublishingIntegrationTests-bootWarCanBePublished.gradle @@ -0,0 +1,29 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'war' +apply plugin: 'org.springframework.boot' +apply plugin: 'maven-publish' + +bootWar { + mainClass = 'com.example.Application' +} + +group = 'com.example' +version = '1.0' + +publishing { + repositories { + maven { + url "$buildDir/repo" + } + } + publications { + mavenBootWeb(MavenPublication) { + from components.bootWeb + } + } +} From bc543ef08a407dede8102080632439ebc6b7144c Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 16 Mar 2017 22:15:31 +0000 Subject: [PATCH 09/30] Use a convention mapping for resolving main class from the classpath --- ...ssSupplier.java => MainClassResolver.java} | 51 ++++++------------- .../gradle/bundling/BootArchiveSupport.java | 10 ++-- .../boot/gradle/bundling/BootJar.java | 10 +--- .../boot/gradle/bundling/BootWar.java | 12 ++--- .../bundling/BundlingPluginFeatures.java | 7 +++ .../boot/gradle/run/BootRunTask.java | 15 ------ .../boot/gradle/run/RunPluginFeatures.java | 4 ++ 7 files changed, 34 insertions(+), 75 deletions(-) rename spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/{MainClassSupplier.java => MainClassResolver.java} (53%) diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/MainClassSupplier.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/MainClassResolver.java similarity index 53% rename from spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/MainClassSupplier.java rename to spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/MainClassResolver.java index 0b493f1a672..aa85cbf10a2 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/MainClassSupplier.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/MainClassResolver.java @@ -19,52 +19,40 @@ package org.springframework.boot.gradle; import java.io.File; import java.io.IOException; import java.util.Objects; -import java.util.function.Supplier; import org.gradle.api.file.FileCollection; import org.springframework.boot.loader.tools.MainClassFinder; /** - * Supplies the main class for an application by returning a configured main class if - * available. If a main class is not available, directories in the application's classpath - * are searched. + * Resolves the main class for an application. * * @author Andy Wilkinson */ -public class MainClassSupplier implements Supplier { +public class MainClassResolver { private static final String SPRING_BOOT_APPLICATION_CLASS_NAME = "org.springframework.boot.autoconfigure.SpringBootApplication"; - private final Supplier classpathSupplier; - - private String mainClass; + private final FileCollection classpath; /** - * Creates a new {@code MainClassSupplier} that will fall back to searching - * directories in the classpath supplied by the given {@code classpathSupplier} for + * Creates a new {@code MainClassResolver} that will search + * directories in the given {@code classpath} for * the application's main class. * - * @param classpathSupplier the supplier of the classpath + * @param classpath the classpath */ - public MainClassSupplier(Supplier classpathSupplier) { - this.classpathSupplier = classpathSupplier; - } - - @Override - public String get() { - if (this.mainClass == null) { - this.mainClass = findMainClass(); - } - return this.mainClass; + public MainClassResolver(FileCollection classpath) { + this.classpath = classpath; } - private String findMainClass() { - FileCollection classpath = this.classpathSupplier.get(); - if (classpath == null) { - return null; - } - return classpath.filter(File::isDirectory).getFiles().stream() + /** + * Resolves the main class. + * + * @return the main class or {@code null} + */ + public String resolveMainClass() { + return this.classpath.filter(File::isDirectory).getFiles().stream() .map(this::findMainClass).filter(Objects::nonNull).findFirst() .orElse(null); } @@ -80,13 +68,4 @@ public class MainClassSupplier implements Supplier { } } - /** - * Sets the {@code mainClass} that will be supplied. - * - * @param mainClass the main class to supply - */ - public void setMainClass(String mainClass) { - this.mainClass = mainClass; - } - } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchiveSupport.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchiveSupport.java index 2dbc1040a9c..7f246220815 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchiveSupport.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchiveSupport.java @@ -37,25 +37,21 @@ class BootArchiveSupport { private final PatternSet requiresUnpack = new PatternSet(); - private final MainClassSupplier mainClassSupplier; - private final Set storedPathPrefixes; private String loaderMainClass; private LaunchScriptConfiguration launchScript = new LaunchScriptConfiguration(); - BootArchiveSupport(MainClassSupplier mainClassSupplier, - String... storedPathPrefixes) { - this.mainClassSupplier = mainClassSupplier; + BootArchiveSupport(String... storedPathPrefixes) { this.storedPathPrefixes = new HashSet<>(Arrays.asList(storedPathPrefixes)); this.requiresUnpack.include(Specs.satisfyNone()); } - void configureManifest(Jar jar) { + void configureManifest(Jar jar, String mainClass) { Attributes attributes = jar.getManifest().getAttributes(); attributes.putIfAbsent("Main-Class", this.loaderMainClass); - attributes.putIfAbsent("Start-Class", this.mainClassSupplier.get()); + attributes.putIfAbsent("Start-Class", mainClass); } CopyAction createCopyAction(Jar jar) { diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootJar.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootJar.java index 03aaeaaaeb7..7625ff3cf6b 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootJar.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootJar.java @@ -28,8 +28,6 @@ import org.gradle.api.internal.file.copy.CopyAction; import org.gradle.api.specs.Spec; import org.gradle.api.tasks.bundling.Jar; -import org.springframework.boot.gradle.MainClassSupplier; - /** * A custom {@link Jar} task that produces a Spring Boot executable jar. * @@ -37,10 +35,7 @@ import org.springframework.boot.gradle.MainClassSupplier; */ public class BootJar extends Jar implements BootArchive { - private final MainClassSupplier mainClassSupplier = new MainClassSupplier( - this::getClasspath); - - private BootArchiveSupport support = new BootArchiveSupport(this.mainClassSupplier, "BOOT-INF/lib"); + private BootArchiveSupport support = new BootArchiveSupport("BOOT-INF/lib"); private FileCollection classpath; @@ -65,7 +60,7 @@ public class BootJar extends Jar implements BootArchive { @Override public void copy() { - this.support.configureManifest(this); + this.support.configureManifest(this, getMainClass()); super.copy(); } @@ -82,7 +77,6 @@ public class BootJar extends Jar implements BootArchive { @Override public void setMainClass(String mainClass) { this.mainClass = mainClass; - this.mainClassSupplier.setMainClass(mainClass); } @Override diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootWar.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootWar.java index 33f425ed19f..647f42ebf04 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootWar.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootWar.java @@ -29,8 +29,6 @@ import org.gradle.api.specs.Spec; import org.gradle.api.tasks.Optional; import org.gradle.api.tasks.bundling.War; -import org.springframework.boot.gradle.MainClassSupplier; - /** * A custom {@link War} task that produces a Spring Boot executable war. * @@ -38,11 +36,8 @@ import org.springframework.boot.gradle.MainClassSupplier; */ public class BootWar extends War implements BootArchive { - private final MainClassSupplier mainClassSupplier = new MainClassSupplier( - this::getClasspath); - - private final BootArchiveSupport support = new BootArchiveSupport( - this.mainClassSupplier, "WEB-INF/lib/", "WEB-INF/lib-provided"); + private final BootArchiveSupport support = new BootArchiveSupport("WEB-INF/lib/", + "WEB-INF/lib-provided"); private String mainClass; @@ -60,7 +55,7 @@ public class BootWar extends War implements BootArchive { @Override public void copy() { - this.support.configureManifest(this); + this.support.configureManifest(this, getMainClass()); super.copy(); } @@ -77,7 +72,6 @@ public class BootWar extends War implements BootArchive { @Override public void setMainClass(String mainClass) { this.mainClass = mainClass; - this.mainClassSupplier.setMainClass(mainClass); } @Override diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BundlingPluginFeatures.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BundlingPluginFeatures.java index 92135c6ff8e..5baa4a9cbcf 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BundlingPluginFeatures.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BundlingPluginFeatures.java @@ -37,6 +37,7 @@ import org.gradle.api.plugins.WarPlugin; import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.Upload; +import org.springframework.boot.gradle.MainClassResolver; import org.springframework.boot.gradle.PluginFeatures; /** @@ -65,6 +66,9 @@ public class BundlingPluginFeatures implements PluginFeatures { ArchivePublishArtifact artifact = new ArchivePublishArtifact(bootWar); this.singlePublishedArtifact.addCandidate(artifact); project.getComponents().add(new BootSoftwareComponent(artifact, "bootWeb")); + bootWar.conventionMapping("mainClass", () -> { + return new MainClassResolver(bootWar.getClasspath()).resolveMainClass(); + }); } private void configureBootJarTask(Project project) { @@ -79,6 +83,9 @@ public class BundlingPluginFeatures implements PluginFeatures { ArchivePublishArtifact artifact = new ArchivePublishArtifact(bootJar); this.singlePublishedArtifact.addCandidate(artifact); project.getComponents().add(new BootSoftwareComponent(artifact, "bootJava")); + bootJar.conventionMapping("mainClass", () -> { + return new MainClassResolver(bootJar.getClasspath()).resolveMainClass(); + }); } private void configureBootArchivesUpload(Project project) { diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/BootRunTask.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/BootRunTask.java index 9f40aacdb9c..c5808cd0d1b 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/BootRunTask.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/BootRunTask.java @@ -26,7 +26,6 @@ import org.gradle.api.internal.file.collections.SimpleFileCollection; import org.gradle.api.tasks.JavaExec; import org.gradle.api.tasks.SourceSet; -import org.springframework.boot.gradle.MainClassSupplier; import org.springframework.boot.loader.tools.FileUtils; /** @@ -38,9 +37,6 @@ import org.springframework.boot.loader.tools.FileUtils; */ public class BootRunTask extends JavaExec { - private final MainClassSupplier mainClassSupplier = new MainClassSupplier( - this::getClasspath); - /** * Whether or not resources (typically in {@code src/main/resources} are added * directly to the classpath. When enabled, this allows live in-place editing of @@ -67,17 +63,6 @@ public class BootRunTask extends JavaExec { super.exec(); } - @Override - public String getMain() { - return this.mainClassSupplier.get(); - } - - @Override - public JavaExec setMain(String mainClassName) { - this.mainClassSupplier.setMainClass(mainClassName); - return super.setMain(mainClassName); - } - private void addResourcesIfNecessary() { if (this.addResources) { SourceSet mainSourceSet = SourceSets.findMainSourceSet(getProject()); diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/RunPluginFeatures.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/RunPluginFeatures.java index 41b335b83ea..17e243334d7 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/RunPluginFeatures.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/RunPluginFeatures.java @@ -23,6 +23,7 @@ import org.gradle.api.Project; import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPluginConvention; +import org.springframework.boot.gradle.MainClassResolver; import org.springframework.boot.gradle.PluginFeatures; /** @@ -60,6 +61,9 @@ public class RunPluginFeatures implements PluginFeatures { return Collections.emptyList(); } }); + run.conventionMapping("main", () -> { + return new MainClassResolver(run.getClasspath()).resolveMainClass(); + }); } } From d015714cba4e696471a0ec8f12c9a2ad6a0a689f Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 21 Mar 2017 16:16:30 +0000 Subject: [PATCH 10/30] Add support for reproducible archives to BootJar and BootWar Closes gh-8391 --- .../gradle/bundling/BootArchiveSupport.java | 34 ++++++++++++++- .../gradle/bundling/BootZipCopyAction.java | 31 +++++++++++--- .../bundling/AbstractBootArchiveTests.java | 42 +++++++++++++++++++ 3 files changed, 100 insertions(+), 7 deletions(-) diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchiveSupport.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchiveSupport.java index 7f246220815..2c40538c0b9 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchiveSupport.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchiveSupport.java @@ -18,13 +18,19 @@ package org.springframework.boot.gradle.bundling; import java.util.Arrays; import java.util.HashSet; +import java.util.Map; import java.util.Set; +import java.util.TreeMap; import org.gradle.api.file.FileTreeElement; +import org.gradle.api.file.RelativePath; import org.gradle.api.internal.file.copy.CopyAction; +import org.gradle.api.internal.file.copy.CopyActionProcessingStream; +import org.gradle.api.internal.file.copy.FileCopyDetailsInternal; import org.gradle.api.java.archives.Attributes; import org.gradle.api.specs.Spec; import org.gradle.api.specs.Specs; +import org.gradle.api.tasks.WorkResult; import org.gradle.api.tasks.bundling.Jar; import org.gradle.api.tasks.util.PatternSet; @@ -55,8 +61,13 @@ class BootArchiveSupport { } CopyAction createCopyAction(Jar jar) { - return new BootZipCopyAction(jar.getArchivePath(), this::requiresUnpacking, + CopyAction copyAction = new BootZipCopyAction(jar.getArchivePath(), + jar.isPreserveFileTimestamps(), this::requiresUnpacking, this.launchScript, this.storedPathPrefixes); + if (!jar.isReproducibleFileOrder()) { + return copyAction; + } + return new ReproducibleOrderingCopyAction(copyAction); } private boolean requiresUnpacking(FileTreeElement fileTreeElement) { @@ -87,4 +98,25 @@ class BootArchiveSupport { this.requiresUnpack.include(spec); } + private static final class ReproducibleOrderingCopyAction implements CopyAction { + + private final CopyAction delegate; + + private ReproducibleOrderingCopyAction(CopyAction delegate) { + this.delegate = delegate; + } + + @Override + public WorkResult execute(CopyActionProcessingStream stream) { + return this.delegate.execute((action) -> { + Map detailsByPath = new TreeMap<>(); + stream.process((details) -> { + detailsByPath.put(details.getRelativePath(), details); + }); + detailsByPath.values().stream().forEach(action::processFile); + }); + } + + } + } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootZipCopyAction.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootZipCopyAction.java index 652c12ad6dd..b29e224c86c 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootZipCopyAction.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootZipCopyAction.java @@ -28,6 +28,7 @@ import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; import org.gradle.api.GradleException; +import org.gradle.api.file.FileCopyDetails; import org.gradle.api.file.FileTreeElement; import org.gradle.api.internal.file.CopyActionProcessingStreamAction; import org.gradle.api.internal.file.copy.CopyAction; @@ -35,6 +36,7 @@ import org.gradle.api.internal.file.copy.CopyActionProcessingStream; import org.gradle.api.internal.file.copy.FileCopyDetailsInternal; import org.gradle.api.specs.Spec; import org.gradle.api.tasks.WorkResult; +import org.gradle.util.GUtil; import org.springframework.boot.loader.tools.DefaultLaunchScript; import org.springframework.boot.loader.tools.FileUtils; @@ -49,15 +51,19 @@ class BootZipCopyAction implements CopyAction { private final File output; + private final boolean preserveFileTimestamps; + private final Spec requiresUnpack; private final LaunchScriptConfiguration launchScript; private final Set storedPathPrefixes; - BootZipCopyAction(File output, Spec requiresUnpack, - LaunchScriptConfiguration launchScript, Set storedPathPrefixes) { + BootZipCopyAction(File output, boolean preserveFileTimestamps, + Spec requiresUnpack, LaunchScriptConfiguration launchScript, + Set storedPathPrefixes) { this.output = output; + this.preserveFileTimestamps = preserveFileTimestamps; this.requiresUnpack = requiresUnpack; this.launchScript = launchScript; this.storedPathPrefixes = storedPathPrefixes; @@ -77,7 +83,8 @@ class BootZipCopyAction implements CopyAction { } try { stream.process(new ZipStreamAction(zipStream, this.output, - this.requiresUnpack, this.storedPathPrefixes)); + this.preserveFileTimestamps, this.requiresUnpack, + this.storedPathPrefixes)); } finally { try { @@ -100,6 +107,9 @@ class BootZipCopyAction implements CopyAction { byte[] buffer = new byte[4096]; while ((entry = in.getNextEntry()) != null) { if (entry.getName().endsWith((".class"))) { + if (!this.preserveFileTimestamps) { + entry.setTime(GUtil.CONSTANT_TIME_FOR_ZIP_ENTRIES); + } out.putNextEntry(entry); int read; while ((read = in.read(buffer)) > 0) { @@ -134,14 +144,18 @@ class BootZipCopyAction implements CopyAction { private final File output; + private final boolean preserveFileTimestamps; + private final Spec requiresUnpack; private final Set storedPathPrefixes; private ZipStreamAction(ZipOutputStream zipStream, File output, - Spec requiresUnpack, Set storedPathPrefixes) { + boolean preserveFileTimestamps, Spec requiresUnpack, + Set storedPathPrefixes) { this.zipStream = zipStream; this.output = output; + this.preserveFileTimestamps = preserveFileTimestamps; this.requiresUnpack = requiresUnpack; this.storedPathPrefixes = storedPathPrefixes; } @@ -165,7 +179,7 @@ class BootZipCopyAction implements CopyAction { private void createDirectory(FileCopyDetailsInternal details) throws IOException { ZipEntry archiveEntry = new ZipEntry( details.getRelativePath().getPathString() + '/'); - archiveEntry.setTime(details.getLastModified()); + archiveEntry.setTime(getTime(details)); this.zipStream.putNextEntry(archiveEntry); this.zipStream.closeEntry(); } @@ -173,7 +187,7 @@ class BootZipCopyAction implements CopyAction { private void createFile(FileCopyDetailsInternal details) throws IOException { String relativePath = details.getRelativePath().getPathString(); ZipEntry archiveEntry = new ZipEntry(relativePath); - archiveEntry.setTime(details.getLastModified()); + archiveEntry.setTime(getTime(details)); this.zipStream.putNextEntry(archiveEntry); if (isStoredEntry(relativePath)) { archiveEntry.setMethod(ZipEntry.STORED); @@ -202,6 +216,11 @@ class BootZipCopyAction implements CopyAction { return false; } + private long getTime(FileCopyDetails details) { + return this.preserveFileTimestamps ? details.getLastModified() + : GUtil.CONSTANT_TIME_FOR_ZIP_ENTRIES; + } + } /** diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveTests.java index 000691d54d9..6a1c2cc7fd4 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveTests.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveTests.java @@ -20,13 +20,18 @@ import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.StandardOpenOption; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Enumeration; +import java.util.List; +import java.util.jar.JarEntry; import java.util.jar.JarFile; import org.gradle.api.Project; import org.gradle.api.tasks.bundling.AbstractArchiveTask; import org.gradle.api.tasks.bundling.Jar; import org.gradle.testfixtures.ProjectBuilder; +import org.gradle.util.GUtil; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -220,6 +225,43 @@ public abstract class AbstractBootArchiveTests { } } + @Test + public void fileTimestampPreservationCanBeDisabled() throws IOException { + this.task.setMainClass("com.example.Main"); + this.task.setPreserveFileTimestamps(false); + this.task.execute(); + assertThat(this.task.getArchivePath().exists()); + try (JarFile jarFile = new JarFile(this.task.getArchivePath())) { + Enumeration entries = jarFile.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + assertThat(entry.getTime()) + .isEqualTo(GUtil.CONSTANT_TIME_FOR_ZIP_ENTRIES); + } + } + } + + @Test + public void reproducibleOrderingCanBeEnabled() throws IOException { + this.task.setMainClass("com.example.Main"); + this.task.from(this.temp.newFile("bravo.txt"), this.temp.newFile("alpha.txt"), + this.temp.newFile("charlie.txt")); + this.task.setReproducibleFileOrder(true); + this.task.execute(); + assertThat(this.task.getArchivePath().exists()); + List textFiles = new ArrayList<>(); + try (JarFile jarFile = new JarFile(this.task.getArchivePath())) { + Enumeration entries = jarFile.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + if (entry.getName().endsWith(".txt")) { + textFiles.add(entry.getName()); + } + } + } + assertThat(textFiles).containsExactly("alpha.txt", "bravo.txt", "charlie.txt"); + } + private T configure(T task) throws IOException { AbstractArchiveTask archiveTask = task; archiveTask.setBaseName("test"); From 6e7e42459b1e41d6a6420c18845ad4ae108ac54d Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 21 Mar 2017 17:50:42 +0000 Subject: [PATCH 11/30] Exclude Devtools by default and provide an option to include it --- .../boot/gradle/bundling/BootArchive.java | 18 +++++++++++ .../gradle/bundling/BootArchiveSupport.java | 31 +++++++++++++++---- .../boot/gradle/bundling/BootJar.java | 10 ++++++ .../boot/gradle/bundling/BootWar.java | 10 ++++++ .../gradle/bundling/BootZipCopyAction.java | 17 +++++++--- .../bundling/AbstractBootArchiveTests.java | 25 +++++++++++++++ .../boot/gradle/bundling/BootWarTests.java | 29 +++++++++++++++++ 7 files changed, 130 insertions(+), 10 deletions(-) diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchive.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchive.java index 46c8db3cd50..3ab193e4933 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchive.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchive.java @@ -99,4 +99,22 @@ public interface BootArchive extends Task { */ void classpath(Object... classpath); + /** + * Returns {@code true} if the Devtools jar should be excluded, otherwise + * {@code false}. + * + * @return {@code true} if the Devtools jar should be excluded, or {@code false} if + * not + */ + @Input + boolean isExcludeDevtools(); + + /** + * Sets whether or not the Devtools jar should be excluded. + * + * @param excludeDevtools {@code true} if the Devtools jar should be excluded, or + * {@code false} if not + */ + void setExcludeDevtools(boolean excludeDevtools); + } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchiveSupport.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchiveSupport.java index 2c40538c0b9..99d5ef3fe4c 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchiveSupport.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchiveSupport.java @@ -45,13 +45,18 @@ class BootArchiveSupport { private final Set storedPathPrefixes; + private final PatternSet exclusions = new PatternSet(); + private String loaderMainClass; private LaunchScriptConfiguration launchScript = new LaunchScriptConfiguration(); + private boolean excludeDevtools = true; + BootArchiveSupport(String... storedPathPrefixes) { this.storedPathPrefixes = new HashSet<>(Arrays.asList(storedPathPrefixes)); this.requiresUnpack.include(Specs.satisfyNone()); + configureExclusions(); } void configureManifest(Jar jar, String mainClass) { @@ -62,18 +67,15 @@ class BootArchiveSupport { CopyAction createCopyAction(Jar jar) { CopyAction copyAction = new BootZipCopyAction(jar.getArchivePath(), - jar.isPreserveFileTimestamps(), this::requiresUnpacking, - this.launchScript, this.storedPathPrefixes); + jar.isPreserveFileTimestamps(), this.requiresUnpack.getAsSpec(), + this.exclusions.getAsExcludeSpec(), this.launchScript, + this.storedPathPrefixes); if (!jar.isReproducibleFileOrder()) { return copyAction; } return new ReproducibleOrderingCopyAction(copyAction); } - private boolean requiresUnpacking(FileTreeElement fileTreeElement) { - return this.requiresUnpack.getAsSpec().isSatisfiedBy(fileTreeElement); - } - String getLoaderMainClass() { return this.loaderMainClass; } @@ -98,6 +100,23 @@ class BootArchiveSupport { this.requiresUnpack.include(spec); } + boolean isExcludeDevtools() { + return this.excludeDevtools; + } + + void setExcludeDevtools(boolean excludeDevtools) { + this.excludeDevtools = excludeDevtools; + configureExclusions(); + } + + private void configureExclusions() { + Set excludes = new HashSet(); + if (this.excludeDevtools) { + excludes.add("**/spring-boot-devtools-*.jar"); + } + this.exclusions.setExcludes(excludes); + } + private static final class ReproducibleOrderingCopyAction implements CopyAction { private final CopyAction delegate; diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootJar.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootJar.java index 7625ff3cf6b..f0af0aed7f3 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootJar.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootJar.java @@ -112,4 +112,14 @@ public class BootJar extends Jar implements BootArchive { classpath); } + @Override + public boolean isExcludeDevtools() { + return this.support.isExcludeDevtools(); + } + + @Override + public void setExcludeDevtools(boolean excludeDevtools) { + this.support.setExcludeDevtools(excludeDevtools); + } + } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootWar.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootWar.java index 647f42ebf04..59c96290b8e 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootWar.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootWar.java @@ -112,4 +112,14 @@ public class BootWar extends War implements BootArchive { classpath); } + @Override + public boolean isExcludeDevtools() { + return this.support.isExcludeDevtools(); + } + + @Override + public void setExcludeDevtools(boolean excludeDevtools) { + this.support.setExcludeDevtools(excludeDevtools); + } + } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootZipCopyAction.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootZipCopyAction.java index b29e224c86c..a49efae2b0b 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootZipCopyAction.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootZipCopyAction.java @@ -55,16 +55,19 @@ class BootZipCopyAction implements CopyAction { private final Spec requiresUnpack; + private final Spec exclusions; + private final LaunchScriptConfiguration launchScript; private final Set storedPathPrefixes; BootZipCopyAction(File output, boolean preserveFileTimestamps, - Spec requiresUnpack, LaunchScriptConfiguration launchScript, - Set storedPathPrefixes) { + Spec requiresUnpack, Spec exclusions, + LaunchScriptConfiguration launchScript, Set storedPathPrefixes) { this.output = output; this.preserveFileTimestamps = preserveFileTimestamps; this.requiresUnpack = requiresUnpack; + this.exclusions = exclusions; this.launchScript = launchScript; this.storedPathPrefixes = storedPathPrefixes; } @@ -83,7 +86,7 @@ class BootZipCopyAction implements CopyAction { } try { stream.process(new ZipStreamAction(zipStream, this.output, - this.preserveFileTimestamps, this.requiresUnpack, + this.preserveFileTimestamps, this.requiresUnpack, this.exclusions, this.storedPathPrefixes)); } finally { @@ -148,20 +151,26 @@ class BootZipCopyAction implements CopyAction { private final Spec requiresUnpack; + private final Spec exclusions; + private final Set storedPathPrefixes; private ZipStreamAction(ZipOutputStream zipStream, File output, boolean preserveFileTimestamps, Spec requiresUnpack, - Set storedPathPrefixes) { + Spec exclusions, Set storedPathPrefixes) { this.zipStream = zipStream; this.output = output; this.preserveFileTimestamps = preserveFileTimestamps; this.requiresUnpack = requiresUnpack; + this.exclusions = exclusions; this.storedPathPrefixes = storedPathPrefixes; } @Override public void processFile(FileCopyDetailsInternal details) { + if (this.exclusions.isSatisfiedBy(details)) { + return; + } try { if (details.isDirectory()) { createDirectory(details); diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveTests.java index 6a1c2cc7fd4..ce7a164a11f 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveTests.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveTests.java @@ -262,6 +262,31 @@ public abstract class AbstractBootArchiveTests { assertThat(textFiles).containsExactly("alpha.txt", "bravo.txt", "charlie.txt"); } + @Test + public void devtoolsJarIsExcludedByDefault() throws IOException { + this.task.setMainClass("com.example.Main"); + this.task.classpath(this.temp.newFile("spring-boot-devtools-0.1.2.jar")); + this.task.execute(); + assertThat(this.task.getArchivePath().exists()); + try (JarFile jarFile = new JarFile(this.task.getArchivePath())) { + assertThat(jarFile.getEntry(this.libPath + "/spring-boot-devtools-0.1.2.jar")) + .isNull(); + } + } + + @Test + public void devtoolsJarCanBeIncluded() throws IOException { + this.task.setMainClass("com.example.Main"); + this.task.classpath(this.temp.newFile("spring-boot-devtools-0.1.2.jar")); + this.task.setExcludeDevtools(false); + this.task.execute(); + assertThat(this.task.getArchivePath().exists()); + try (JarFile jarFile = new JarFile(this.task.getArchivePath())) { + assertThat(jarFile.getEntry(this.libPath + "/spring-boot-devtools-0.1.2.jar")) + .isNotNull(); + } + } + private T configure(T task) throws IOException { AbstractArchiveTask archiveTask = task; archiveTask.setBaseName("test"); diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootWarTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootWarTests.java index 383d6350d15..8278678b166 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootWarTests.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootWarTests.java @@ -47,4 +47,33 @@ public class BootWarTests extends AbstractBootArchiveTests { } } + @Test + public void devtoolsJarIsExcludedByDefaultWhenItsOnTheProvidedClasspath() + throws IOException { + getTask().setMainClass("com.example.Main"); + getTask().providedClasspath(this.temp.newFile("spring-boot-devtools-0.1.2.jar")); + getTask().execute(); + assertThat(getTask().getArchivePath().exists()); + try (JarFile jarFile = new JarFile(getTask().getArchivePath())) { + assertThat(jarFile + .getEntry("WEB-INF/lib-provided/spring-boot-devtools-0.1.2.jar")) + .isNull(); + } + } + + @Test + public void devtoolsJarCanBeIncludedWhenItsOnTheProvidedClasspath() + throws IOException { + getTask().setMainClass("com.example.Main"); + getTask().providedClasspath(this.temp.newFile("spring-boot-devtools-0.1.2.jar")); + getTask().setExcludeDevtools(false); + getTask().execute(); + assertThat(getTask().getArchivePath().exists()); + try (JarFile jarFile = new JarFile(getTask().getArchivePath())) { + assertThat(jarFile + .getEntry("WEB-INF/lib-provided/spring-boot-devtools-0.1.2.jar")) + .isNotNull(); + } + } + } From 2b44ad9809d9fe76e7766793cadbc5deea8c88d6 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 27 Mar 2017 15:06:50 +0100 Subject: [PATCH 12/30] Rework BootRunTask to be more idiomatic and make fewer assumptions --- .../boot/gradle/run/BootRun.java | 54 ++++++++++++ .../boot/gradle/run/BootRunTask.java | 87 ------------------- .../boot/gradle/run/RunPluginFeatures.java | 8 +- .../java/com/example/BootRunApplication.java | 42 +++++++++ .../gradle/run/BootRunIntegrationTests.java | 74 ++++++++++++++++ .../boot/gradle/testkit/GradleBuild.java | 2 + ...tRunIntegrationTests-basicExecution.gradle | 8 ++ ...ationTests-sourceResourcesCanBeUsed.gradle | 12 +++ 8 files changed, 197 insertions(+), 90 deletions(-) create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/BootRun.java delete mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/BootRunTask.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/java/com/example/BootRunApplication.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/run/BootRunIntegrationTests.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/run/BootRunIntegrationTests-basicExecution.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/run/BootRunIntegrationTests-sourceResourcesCanBeUsed.gradle diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/BootRun.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/BootRun.java new file mode 100644 index 00000000000..527e8336c2b --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/BootRun.java @@ -0,0 +1,54 @@ +/* + * Copyright 2012-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.boot.gradle.run; + +import org.gradle.api.file.SourceDirectorySet; +import org.gradle.api.tasks.JavaExec; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.SourceSetOutput; + +/** + * Custom {@link JavaExec} task for running a Spring Boot application. + * + * @author Andy Wilkinson + */ +public class BootRun extends JavaExec { + + /** + * Adds the {@link SourceDirectorySet#getSrcDirs() source directories} of the given + * {@code sourceSet's} {@link SourceSet#getResources() resources} to the start of the + * classpath in place of the {@link SourceSet#getOutput output's} + * {@link SourceSetOutput#getResourcesDir() resources directory}. + * + * @param sourceSet the source set + */ + public void sourceResources(SourceSet sourceSet) { + setClasspath(getProject() + .files(sourceSet.getResources().getSrcDirs(), getClasspath()) + .filter((file) -> !file.equals(sourceSet.getOutput().getResourcesDir()))); + } + + @Override + public void exec() { + if (System.console() != null) { + // Record that the console is available here for AnsiOutput to detect later + this.getEnvironment().put("spring.output.ansi.console-available", true); + } + super.exec(); + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/BootRunTask.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/BootRunTask.java deleted file mode 100644 index c5808cd0d1b..00000000000 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/BootRunTask.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2012-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.boot.gradle.run; - -import java.io.File; -import java.util.ArrayList; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; - -import org.gradle.api.internal.file.collections.SimpleFileCollection; -import org.gradle.api.tasks.JavaExec; -import org.gradle.api.tasks.SourceSet; - -import org.springframework.boot.loader.tools.FileUtils; - -/** - * Extension of the standard 'run' task with additional Spring Boot features. - * - * @author Dave Syer - * @author Phillip Webb - * @author Andy Wilkinson - */ -public class BootRunTask extends JavaExec { - - /** - * Whether or not resources (typically in {@code src/main/resources} are added - * directly to the classpath. When enabled, this allows live in-place editing of - * resources. Duplicate resources are removed from the resource output directory to - * prevent them from appearing twice if {@code ClassLoader.getResources()} is called. - */ - private boolean addResources = false; - - public boolean getAddResources() { - return this.addResources; - } - - public void setAddResources(boolean addResources) { - this.addResources = addResources; - } - - @Override - public void exec() { - if (System.console() != null) { - // Record that the console is available here for AnsiOutput to detect later - this.getEnvironment().put("spring.output.ansi.console-available", true); - } - addResourcesIfNecessary(); - super.exec(); - } - - private void addResourcesIfNecessary() { - if (this.addResources) { - SourceSet mainSourceSet = SourceSets.findMainSourceSet(getProject()); - final File outputDir = (mainSourceSet == null ? null - : mainSourceSet.getOutput().getResourcesDir()); - final Set resources = new LinkedHashSet<>(); - if (mainSourceSet != null) { - resources.addAll(mainSourceSet.getResources().getSrcDirs()); - } - List classPath = new ArrayList<>(getClasspath().getFiles()); - classPath.addAll(0, resources); - getLogger().info("Adding classpath: " + resources); - setClasspath(new SimpleFileCollection(classPath)); - if (outputDir != null) { - for (File directory : resources) { - FileUtils.removeDuplicatesFromOutputDirectory(outputDir, directory); - } - } - } - } - -} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/RunPluginFeatures.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/RunPluginFeatures.java index 17e243334d7..0ad7034b4f7 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/RunPluginFeatures.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/RunPluginFeatures.java @@ -22,6 +22,7 @@ import java.util.concurrent.Callable; import org.gradle.api.Project; import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPluginConvention; +import org.gradle.api.tasks.SourceSet; import org.springframework.boot.gradle.MainClassResolver; import org.springframework.boot.gradle.PluginFeatures; @@ -30,6 +31,7 @@ import org.springframework.boot.gradle.PluginFeatures; * {@link PluginFeatures} to add run support. * * @author Phillip Webb + * @author Andy Wilkinson */ public class RunPluginFeatures implements PluginFeatures { @@ -46,12 +48,12 @@ public class RunPluginFeatures implements PluginFeatures { final JavaPluginConvention javaConvention = project.getConvention() .getPlugin(JavaPluginConvention.class); - BootRunTask run = project.getTasks().create(RUN_APP_TASK_NAME, BootRunTask.class); + BootRun run = project.getTasks().create(RUN_APP_TASK_NAME, BootRun.class); run.setDescription("Run the project with support for " + "auto-detecting main class and reloading static resources"); run.setGroup("application"); - run.setClasspath( - javaConvention.getSourceSets().findByName("main").getRuntimeClasspath()); + run.classpath(javaConvention.getSourceSets() + .findByName(SourceSet.MAIN_SOURCE_SET_NAME).getRuntimeClasspath()); run.getConventionMapping().map("jvmArgs", new Callable() { @Override public Object call() throws Exception { diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/com/example/BootRunApplication.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/com/example/BootRunApplication.java new file mode 100644 index 00000000000..95cc04e06fe --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/com/example/BootRunApplication.java @@ -0,0 +1,42 @@ +/* + * Copyright 2012-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 com.example; + +import java.net.URL; +import java.net.URLClassLoader; + +/** + * Very basic application used for testing {@code BootRun}. + * + * @author Andy Wilkinson + */ +public class BootRunApplication { + + protected BootRunApplication() { + + } + + public static void main(String[] args) { + int i = 1; + for (URL url : ((URLClassLoader) BootRunApplication.class.getClassLoader()) + .getURLs()) { + System.out.println(i++ + ". " + url); + } + + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/run/BootRunIntegrationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/run/BootRunIntegrationTests.java new file mode 100644 index 00000000000..5945b21551f --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/run/BootRunIntegrationTests.java @@ -0,0 +1,74 @@ +/* + * Copyright 2012-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.boot.gradle.run; + +import java.io.File; +import java.io.IOException; + +import org.gradle.testkit.runner.BuildResult; +import org.gradle.testkit.runner.TaskOutcome; +import org.junit.Rule; +import org.junit.Test; + +import org.springframework.boot.gradle.testkit.GradleBuild; +import org.springframework.util.FileSystemUtils; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for {@link BootRun}. + * + * @author Andy Wilkinson + */ +public class BootRunIntegrationTests { + + @Rule + public final GradleBuild gradleBuild = new GradleBuild(); + + @Test + public void basicExecution() throws IOException { + File output = new File(this.gradleBuild.getProjectDir(), + "src/main/java/com/example"); + output.mkdirs(); + FileSystemUtils.copyRecursively(new File("src/test/java/com/example"), output); + new File(this.gradleBuild.getProjectDir(), "src/main/resources").mkdirs(); + BuildResult result = this.gradleBuild.build("bootRun"); + assertThat(result.task(":bootRun").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + assertThat(result.getOutput()).contains("1. " + urlOf("build/classes/main")); + assertThat(result.getOutput()).contains("2. " + urlOf("build/resources/main")); + assertThat(result.getOutput()).doesNotContain(urlOf("src/main/resources")); + } + + @Test + public void sourceResourcesCanBeUsed() throws IOException { + File output = new File(this.gradleBuild.getProjectDir(), + "src/main/java/com/example"); + output.mkdirs(); + FileSystemUtils.copyRecursively(new File("src/test/java/com/example"), output); + BuildResult result = this.gradleBuild.build("bootRun"); + assertThat(result.task(":bootRun").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + assertThat(result.getOutput()).contains("1. " + urlOf("src/main/resources")); + assertThat(result.getOutput()).contains("2. " + urlOf("build/classes/main")); + assertThat(result.getOutput()).doesNotContain(urlOf("build/resources/main")); + } + + private String urlOf(String path) throws IOException { + return new File(this.gradleBuild.getProjectDir().getCanonicalFile(), path).toURI() + .toURL().toString(); + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java index 078446016d4..7d5f5eb6c0f 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java @@ -39,6 +39,7 @@ import org.junit.runner.Description; import org.junit.runners.model.Statement; import org.xml.sax.InputSource; +import org.springframework.asm.ClassVisitor; import org.springframework.boot.loader.tools.LaunchScript; /** @@ -106,6 +107,7 @@ public class GradleBuild implements TestRule { return absolutePath("bin") + "," + absolutePath("build/classes/main") + "," + absolutePath("build/resources/main") + "," + pathOfJarContaining(LaunchScript.class) + "," + + pathOfJarContaining(ClassVisitor.class) + "," + pathOfJarContaining(DependencyManagementPlugin.class); } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/run/BootRunIntegrationTests-basicExecution.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/run/BootRunIntegrationTests-basicExecution.gradle new file mode 100644 index 00000000000..97d0d1c7e3c --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/run/BootRunIntegrationTests-basicExecution.gradle @@ -0,0 +1,8 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'java' +apply plugin: 'org.springframework.boot' diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/run/BootRunIntegrationTests-sourceResourcesCanBeUsed.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/run/BootRunIntegrationTests-sourceResourcesCanBeUsed.gradle new file mode 100644 index 00000000000..47504d6d74a --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/run/BootRunIntegrationTests-sourceResourcesCanBeUsed.gradle @@ -0,0 +1,12 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'java' +apply plugin: 'org.springframework.boot' + +bootRun { + sourceResources sourceSets.main +} From 5bf8f778e2d9ad921760ee008a24cc3810ced04a Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 27 Mar 2017 15:30:29 +0100 Subject: [PATCH 13/30] Don't write the default loader classes when a custom launcher is used --- .../gradle/bundling/BootArchiveSupport.java | 31 ++++++++++++------- .../boot/gradle/bundling/BootJar.java | 4 +-- .../boot/gradle/bundling/BootWar.java | 4 +-- .../gradle/bundling/BootZipCopyAction.java | 17 +++++++--- .../bundling/AbstractBootArchiveTests.java | 17 ++++++++++ 5 files changed, 54 insertions(+), 19 deletions(-) diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchiveSupport.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchiveSupport.java index 99d5ef3fe4c..64eb4266c77 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchiveSupport.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchiveSupport.java @@ -17,6 +17,7 @@ package org.springframework.boot.gradle.bundling; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -41,19 +42,30 @@ import org.gradle.api.tasks.util.PatternSet; */ class BootArchiveSupport { + private static final Set DEFAULT_LAUNCHER_CLASSES; + + static { + Set defaultLauncherClasses = new HashSet(); + defaultLauncherClasses.add("org.springframework.boot.loader.JarLauncher"); + defaultLauncherClasses.add("org.springframework.boot.loader.PropertiesLauncher"); + defaultLauncherClasses.add("org.springframework.boot.loader.WarLauncher"); + DEFAULT_LAUNCHER_CLASSES = Collections.unmodifiableSet(defaultLauncherClasses); + } + private final PatternSet requiresUnpack = new PatternSet(); private final Set storedPathPrefixes; private final PatternSet exclusions = new PatternSet(); - private String loaderMainClass; + private final String loaderMainClass; private LaunchScriptConfiguration launchScript = new LaunchScriptConfiguration(); private boolean excludeDevtools = true; - BootArchiveSupport(String... storedPathPrefixes) { + BootArchiveSupport(String loaderMainClass, String... storedPathPrefixes) { + this.loaderMainClass = loaderMainClass; this.storedPathPrefixes = new HashSet<>(Arrays.asList(storedPathPrefixes)); this.requiresUnpack.include(Specs.satisfyNone()); configureExclusions(); @@ -67,21 +79,18 @@ class BootArchiveSupport { CopyAction createCopyAction(Jar jar) { CopyAction copyAction = new BootZipCopyAction(jar.getArchivePath(), - jar.isPreserveFileTimestamps(), this.requiresUnpack.getAsSpec(), - this.exclusions.getAsExcludeSpec(), this.launchScript, - this.storedPathPrefixes); + jar.isPreserveFileTimestamps(), isUsingDefaultLoader(jar), + this.requiresUnpack.getAsSpec(), this.exclusions.getAsExcludeSpec(), + this.launchScript, this.storedPathPrefixes); if (!jar.isReproducibleFileOrder()) { return copyAction; } return new ReproducibleOrderingCopyAction(copyAction); } - String getLoaderMainClass() { - return this.loaderMainClass; - } - - void setLoaderMainClass(String loaderMainClass) { - this.loaderMainClass = loaderMainClass; + private boolean isUsingDefaultLoader(Jar jar) { + return DEFAULT_LAUNCHER_CLASSES + .contains(jar.getManifest().getAttributes().get("Main-Class")); } LaunchScriptConfiguration getLaunchScript() { diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootJar.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootJar.java index f0af0aed7f3..9665c0f8aa3 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootJar.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootJar.java @@ -35,14 +35,14 @@ import org.gradle.api.tasks.bundling.Jar; */ public class BootJar extends Jar implements BootArchive { - private BootArchiveSupport support = new BootArchiveSupport("BOOT-INF/lib"); + private BootArchiveSupport support = new BootArchiveSupport( + "org.springframework.boot.loader.JarLauncher", "BOOT-INF/lib"); private FileCollection classpath; private String mainClass; public BootJar() { - this.support.setLoaderMainClass("org.springframework.boot.loader.JarLauncher"); CopySpec bootInf = getRootSpec().addChildBeforeSpec(getMainSpec()) .into("BOOT-INF"); bootInf.into("lib", classpathFiles(File::isFile)); diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootWar.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootWar.java index 59c96290b8e..8203ffa6fca 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootWar.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootWar.java @@ -36,7 +36,8 @@ import org.gradle.api.tasks.bundling.War; */ public class BootWar extends War implements BootArchive { - private final BootArchiveSupport support = new BootArchiveSupport("WEB-INF/lib/", + private final BootArchiveSupport support = new BootArchiveSupport( + "org.springframework.boot.loader.WarLauncher", "WEB-INF/lib/", "WEB-INF/lib-provided"); private String mainClass; @@ -44,7 +45,6 @@ public class BootWar extends War implements BootArchive { private FileCollection providedClasspath; public BootWar() { - this.support.setLoaderMainClass("org.springframework.boot.loader.WarLauncher"); getWebInf().into("lib-provided", (copySpec) -> { copySpec.from((Callable>) () -> { return this.providedClasspath == null ? Collections.emptyList() diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootZipCopyAction.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootZipCopyAction.java index a49efae2b0b..348f352d4e3 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootZipCopyAction.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootZipCopyAction.java @@ -53,6 +53,8 @@ class BootZipCopyAction implements CopyAction { private final boolean preserveFileTimestamps; + private final boolean includeDefaultLoader; + private final Spec requiresUnpack; private final Spec exclusions; @@ -62,10 +64,12 @@ class BootZipCopyAction implements CopyAction { private final Set storedPathPrefixes; BootZipCopyAction(File output, boolean preserveFileTimestamps, - Spec requiresUnpack, Spec exclusions, - LaunchScriptConfiguration launchScript, Set storedPathPrefixes) { + boolean includeDefaultLoader, Spec requiresUnpack, + Spec exclusions, LaunchScriptConfiguration launchScript, + Set storedPathPrefixes) { this.output = output; this.preserveFileTimestamps = preserveFileTimestamps; + this.includeDefaultLoader = includeDefaultLoader; this.requiresUnpack = requiresUnpack; this.exclusions = exclusions; this.launchScript = launchScript; @@ -79,7 +83,7 @@ class BootZipCopyAction implements CopyAction { FileOutputStream fileStream = new FileOutputStream(this.output); writeLaunchScriptIfNecessary(fileStream); zipStream = new ZipOutputStream(fileStream); - writeLoaderClasses(zipStream); + writeLoaderClassesIfNecessary(zipStream); } catch (IOException ex) { throw new GradleException("Failed to create " + this.output, ex); @@ -102,8 +106,13 @@ class BootZipCopyAction implements CopyAction { }; } - private void writeLoaderClasses(ZipOutputStream out) { + private void writeLoaderClassesIfNecessary(ZipOutputStream out) { + if (this.includeDefaultLoader) { + writeLoaderClasses(out); + } + } + private void writeLoaderClasses(ZipOutputStream out) { ZipEntry entry; try (ZipInputStream in = new ZipInputStream(getClass() .getResourceAsStream("/META-INF/loader/spring-boot-loader.jar"))) { diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveTests.java index ce7a164a11f..876b98d5116 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveTests.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveTests.java @@ -135,6 +135,20 @@ public abstract class AbstractBootArchiveTests { } } + @Test + public void loaderIsWrittenToTheRootOfTheJarWhenUsingThePropertiesLauncher() + throws IOException { + this.task.setMainClass("com.example.Main"); + this.task.execute(); + this.task.getManifest().getAttributes().put("Main-Class", + "org.springframework.boot.loader.PropertiesLauncher"); + try (JarFile jarFile = new JarFile(this.task.getArchivePath())) { + assertThat(jarFile.getEntry( + "org/springframework/boot/loader/LaunchedURLClassLoader.class")) + .isNotNull(); + } + } + @Test public void unpackCommentIsAddedToEntryIdentifiedByAPattern() throws IOException { this.task.setMainClass("com.example.Main"); @@ -207,6 +221,9 @@ public abstract class AbstractBootArchiveTests { .isEqualTo("com.example.CustomLauncher"); assertThat(jarFile.getManifest().getMainAttributes().getValue("Start-Class")) .isEqualTo("com.example.Main"); + assertThat(jarFile.getEntry( + "org/springframework/boot/loader/LaunchedURLClassLoader.class")) + .isNull(); } } From f440b7b7e39c09da940f477a7d05bfaf7f885020 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 28 Mar 2017 10:46:00 +0100 Subject: [PATCH 14/30] Move decision about an entry's compression out into BootJar and BootWar --- .../gradle/bundling/BootArchiveSupport.java | 12 +++--- .../boot/gradle/bundling/BootJar.java | 20 +++++++++- .../boot/gradle/bundling/BootWar.java | 23 ++++++++++- .../gradle/bundling/BootZipCopyAction.java | 29 ++++++-------- .../boot/gradle/bundling/ZipCompression.java | 38 +++++++++++++++++++ 5 files changed, 96 insertions(+), 26 deletions(-) create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/ZipCompression.java diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchiveSupport.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchiveSupport.java index 64eb4266c77..3d8acf6bd6c 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchiveSupport.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchiveSupport.java @@ -16,13 +16,14 @@ package org.springframework.boot.gradle.bundling; -import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.TreeMap; +import java.util.function.Function; +import org.gradle.api.file.FileCopyDetails; import org.gradle.api.file.FileTreeElement; import org.gradle.api.file.RelativePath; import org.gradle.api.internal.file.copy.CopyAction; @@ -54,7 +55,7 @@ class BootArchiveSupport { private final PatternSet requiresUnpack = new PatternSet(); - private final Set storedPathPrefixes; + private final Function compressionResolver; private final PatternSet exclusions = new PatternSet(); @@ -64,9 +65,10 @@ class BootArchiveSupport { private boolean excludeDevtools = true; - BootArchiveSupport(String loaderMainClass, String... storedPathPrefixes) { + BootArchiveSupport(String loaderMainClass, + Function compressionResolver) { this.loaderMainClass = loaderMainClass; - this.storedPathPrefixes = new HashSet<>(Arrays.asList(storedPathPrefixes)); + this.compressionResolver = compressionResolver; this.requiresUnpack.include(Specs.satisfyNone()); configureExclusions(); } @@ -81,7 +83,7 @@ class BootArchiveSupport { CopyAction copyAction = new BootZipCopyAction(jar.getArchivePath(), jar.isPreserveFileTimestamps(), isUsingDefaultLoader(jar), this.requiresUnpack.getAsSpec(), this.exclusions.getAsExcludeSpec(), - this.launchScript, this.storedPathPrefixes); + this.launchScript, this.compressionResolver); if (!jar.isReproducibleFileOrder()) { return copyAction; } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootJar.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootJar.java index 9665c0f8aa3..fc331f6edd1 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootJar.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootJar.java @@ -23,6 +23,7 @@ import java.util.concurrent.Callable; import org.gradle.api.Action; import org.gradle.api.file.CopySpec; import org.gradle.api.file.FileCollection; +import org.gradle.api.file.FileCopyDetails; import org.gradle.api.file.FileTreeElement; import org.gradle.api.internal.file.copy.CopyAction; import org.gradle.api.specs.Spec; @@ -36,7 +37,7 @@ import org.gradle.api.tasks.bundling.Jar; public class BootJar extends Jar implements BootArchive { private BootArchiveSupport support = new BootArchiveSupport( - "org.springframework.boot.loader.JarLauncher", "BOOT-INF/lib"); + "org.springframework.boot.loader.JarLauncher", this::resolveZipCompression); private FileCollection classpath; @@ -122,4 +123,21 @@ public class BootJar extends Jar implements BootArchive { this.support.setExcludeDevtools(excludeDevtools); } + /** + * Returns the {@link ZipCompression} that should be used when adding the file + * represented by the given {@code details} to the jar. + *

+ * By default, any file in {@code BOOT-INF/lib/} is stored and all other files are + * deflated. + * + * @param details the details + * @return the compression to use + */ + protected ZipCompression resolveZipCompression(FileCopyDetails details) { + if (details.getRelativePath().getPathString().startsWith("BOOT-INF/lib/")) { + return ZipCompression.STORED; + } + return ZipCompression.DEFLATED; + } + } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootWar.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootWar.java index 8203ffa6fca..8c243ed8644 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootWar.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootWar.java @@ -23,6 +23,7 @@ import java.util.concurrent.Callable; import org.gradle.api.Action; import org.gradle.api.Project; import org.gradle.api.file.FileCollection; +import org.gradle.api.file.FileCopyDetails; import org.gradle.api.file.FileTreeElement; import org.gradle.api.internal.file.copy.CopyAction; import org.gradle.api.specs.Spec; @@ -37,8 +38,7 @@ import org.gradle.api.tasks.bundling.War; public class BootWar extends War implements BootArchive { private final BootArchiveSupport support = new BootArchiveSupport( - "org.springframework.boot.loader.WarLauncher", "WEB-INF/lib/", - "WEB-INF/lib-provided"); + "org.springframework.boot.loader.WarLauncher", this::resolveZipCompression); private String mainClass; @@ -122,4 +122,23 @@ public class BootWar extends War implements BootArchive { this.support.setExcludeDevtools(excludeDevtools); } + /** + * Returns the {@link ZipCompression} that should be used when adding the file + * represented by the given {@code details} to the jar. + *

+ * By default, any file in {@code WEB-INF/lib/} or {@code WEB-INF/lib-provided/} is + * stored and all other files are deflated. + * + * @param details the details + * @return the compression to use + */ + protected ZipCompression resolveZipCompression(FileCopyDetails details) { + String relativePath = details.getRelativePath().getPathString(); + if (relativePath.startsWith("WEB-INF/lib/") + || relativePath.startsWith("WEB-INF/lib-provided/")) { + return ZipCompression.STORED; + } + return ZipCompression.DEFLATED; + } + } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootZipCopyAction.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootZipCopyAction.java index 348f352d4e3..0eaa6c38902 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootZipCopyAction.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootZipCopyAction.java @@ -21,7 +21,7 @@ import java.io.FileOutputStream; import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; -import java.util.Set; +import java.util.function.Function; import java.util.zip.CRC32; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; @@ -61,19 +61,19 @@ class BootZipCopyAction implements CopyAction { private final LaunchScriptConfiguration launchScript; - private final Set storedPathPrefixes; + private final Function compressionResolver; BootZipCopyAction(File output, boolean preserveFileTimestamps, boolean includeDefaultLoader, Spec requiresUnpack, Spec exclusions, LaunchScriptConfiguration launchScript, - Set storedPathPrefixes) { + Function compressionResolver) { this.output = output; this.preserveFileTimestamps = preserveFileTimestamps; this.includeDefaultLoader = includeDefaultLoader; this.requiresUnpack = requiresUnpack; this.exclusions = exclusions; this.launchScript = launchScript; - this.storedPathPrefixes = storedPathPrefixes; + this.compressionResolver = compressionResolver; } @Override @@ -91,7 +91,7 @@ class BootZipCopyAction implements CopyAction { try { stream.process(new ZipStreamAction(zipStream, this.output, this.preserveFileTimestamps, this.requiresUnpack, this.exclusions, - this.storedPathPrefixes)); + this.compressionResolver)); } finally { try { @@ -162,17 +162,18 @@ class BootZipCopyAction implements CopyAction { private final Spec exclusions; - private final Set storedPathPrefixes; + private final Function compressionType; private ZipStreamAction(ZipOutputStream zipStream, File output, boolean preserveFileTimestamps, Spec requiresUnpack, - Spec exclusions, Set storedPathPrefixes) { + Spec exclusions, + Function compressionType) { this.zipStream = zipStream; this.output = output; this.preserveFileTimestamps = preserveFileTimestamps; this.requiresUnpack = requiresUnpack; this.exclusions = exclusions; - this.storedPathPrefixes = storedPathPrefixes; + this.compressionType = compressionType; } @Override @@ -207,7 +208,8 @@ class BootZipCopyAction implements CopyAction { ZipEntry archiveEntry = new ZipEntry(relativePath); archiveEntry.setTime(getTime(details)); this.zipStream.putNextEntry(archiveEntry); - if (isStoredEntry(relativePath)) { + ZipCompression compression = this.compressionType.apply(details); + if (compression == ZipCompression.STORED) { archiveEntry.setMethod(ZipEntry.STORED); archiveEntry.setSize(details.getSize()); archiveEntry.setCompressedSize(details.getSize()); @@ -225,15 +227,6 @@ class BootZipCopyAction implements CopyAction { this.zipStream.closeEntry(); } - private boolean isStoredEntry(String relativePath) { - for (String prefix : this.storedPathPrefixes) { - if (relativePath.startsWith(prefix)) { - return true; - } - } - return false; - } - private long getTime(FileCopyDetails details) { return this.preserveFileTimestamps ? details.getLastModified() : GUtil.CONSTANT_TIME_FOR_ZIP_ENTRIES; diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/ZipCompression.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/ZipCompression.java new file mode 100644 index 00000000000..b34e0793d85 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/ZipCompression.java @@ -0,0 +1,38 @@ +/* + * Copyright 2012-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.boot.gradle.bundling; + +import java.util.zip.ZipEntry; + +/** + * An enumeration of supported compression options for an entry in a ZIP archive. + * + * @author Andy Wilkinson + */ +public enum ZipCompression { + + /** + * The entry should be {@link ZipEntry#STORED} in the archive. + */ + STORED, + + /** + * The entry should be {@link ZipEntry#DEFLATED} in the archive. + */ + DEFLATED; + +} From 8048791ab21c3999126ffc5368d1120eac72df4e Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 28 Mar 2017 10:59:29 +0100 Subject: [PATCH 15/30] Remove AgentPluginFeatures as they are undocumented The current implementation is undocumented so it's not clear exactly what it's intended to do. It also appears to overreach as, for example, it affects every JavaExec task in a project. We may need to reinstate something that's somewhat equivalent, but I'd like to do that armed with more information about the intent of the functionality and some specific problems that need to be solved. --- .../gradle/agent/AgentPluginFeatures.java | 35 ----- .../boot/gradle/agent/AgentTasksEnhancer.java | 127 ------------------ .../boot/gradle/plugin/SpringBootPlugin.java | 2 - 3 files changed, 164 deletions(-) delete mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/agent/AgentPluginFeatures.java delete mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/agent/AgentTasksEnhancer.java diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/agent/AgentPluginFeatures.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/agent/AgentPluginFeatures.java deleted file mode 100644 index 024d9d5faef..00000000000 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/agent/AgentPluginFeatures.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2012-2015 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.boot.gradle.agent; - -import org.gradle.api.Project; - -import org.springframework.boot.gradle.PluginFeatures; - -/** - * {@link PluginFeatures} to add Java Agent support. - * - * @author Phillip Webb - */ -public class AgentPluginFeatures implements PluginFeatures { - - @Override - public void apply(Project project) { - project.afterEvaluate(new AgentTasksEnhancer()); - } - -} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/agent/AgentTasksEnhancer.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/agent/AgentTasksEnhancer.java deleted file mode 100644 index 77b5447df67..00000000000 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/agent/AgentTasksEnhancer.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2012-2015 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.boot.gradle.agent; - -import java.io.File; -import java.net.URISyntaxException; -import java.security.CodeSource; - -import org.gradle.api.Action; -import org.gradle.api.Project; -import org.gradle.api.Task; -import org.gradle.api.tasks.JavaExec; - -import org.springframework.boot.gradle.SpringBootPluginExtension; - -/** - * Add a java agent to the "run" task if configured. You can add an agent in 3 ways (4 if - * you want to use native gradle features as well): - * - *

    - *
  1. Use "-Prun.agent=[path-to-jar]" on the gradle command line
  2. - *
  3. Add an "agent" property (jar file) to the "springBoot" extension in build.gradle - *
  4. - *
  5. As a special case springloaded is detected as a build script dependency
  6. - *
- * - * @author Dave Syer - * @author Phillip Webb - */ -public class AgentTasksEnhancer implements Action { - - private static final String SPRING_LOADED_AGENT_CLASS_NAME = "org.springsource.loaded.agent.SpringLoadedAgent"; - - private File agent; - - private Boolean noverify; - - @Override - public void execute(Project project) { - setup(project); - if (this.agent != null) { - for (Task task : project.getTasks()) { - addAgent(project, task); - } - } - } - - private void setup(Project project) { - project.getLogger().info("Configuring agent"); - SpringBootPluginExtension extension = project.getExtensions() - .getByType(SpringBootPluginExtension.class); - this.noverify = extension.getNoverify(); - this.agent = getAgent(project, extension); - if (this.agent == null) { - this.agent = getSpringLoadedAgent(); - if (this.noverify == null) { - this.noverify = true; - } - } - project.getLogger().debug("Agent: " + this.agent); - } - - private File getAgent(Project project, SpringBootPluginExtension extension) { - if (project.hasProperty("run.agent")) { - return project.file(project.property("run.agent")); - } - return extension.getAgent(); - } - - private File getSpringLoadedAgent() { - try { - Class loaded = Class.forName(SPRING_LOADED_AGENT_CLASS_NAME); - if (loaded != null) { - CodeSource source = loaded.getProtectionDomain().getCodeSource(); - if (source != null) { - try { - return new File(source.getLocation().toURI()); - } - catch (URISyntaxException ex) { - return new File(source.getLocation().getPath()); - } - } - } - } - catch (ClassNotFoundException ex) { - // ignore; - } - return null; - } - - private void addAgent(Project project, Task task) { - if (task instanceof JavaExec) { - addAgent(project, (JavaExec) task); - } - } - - private void addAgent(Project project, JavaExec exec) { - project.getLogger().debug("Attaching to: " + exec); - if (this.agent != null) { - project.getLogger().info("Attaching agent: " + this.agent); - exec.jvmArgs("-javaagent:" + this.agent.getAbsolutePath()); - if (this.noverify != null && this.noverify) { - exec.jvmArgs("-noverify"); - } - Iterable defaultJvmArgs = exec.getConventionMapping() - .getConventionValue(null, "jvmArgs", false); - if (defaultJvmArgs != null) { - exec.jvmArgs(defaultJvmArgs); - } - } - } - -} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java index 7b1efcb8f49..67efd9754cb 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java @@ -23,7 +23,6 @@ import org.gradle.api.Task; import org.gradle.api.tasks.compile.JavaCompile; import org.springframework.boot.gradle.SpringBootPluginExtension; -import org.springframework.boot.gradle.agent.AgentPluginFeatures; import org.springframework.boot.gradle.application.ApplicationPluginFeatures; import org.springframework.boot.gradle.bundling.BundlingPluginFeatures; import org.springframework.boot.gradle.dependencymanagement.DependencyManagementPluginFeatures; @@ -42,7 +41,6 @@ public class SpringBootPlugin implements Plugin { public void apply(Project project) { project.getExtensions().create("springBoot", SpringBootPluginExtension.class, project); - new AgentPluginFeatures().apply(project); new ApplicationPluginFeatures().apply(project); new BundlingPluginFeatures().apply(project); new RunPluginFeatures().apply(project); From 5708eaf41bb07e38a608104cd08bb8f26e6c6b3c Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 28 Mar 2017 11:04:43 +0100 Subject: [PATCH 16/30] Simplify the DSL extension to provide a solid foundation --- .../boot/gradle/SpringBootExtension.java | 69 ++++ .../gradle/SpringBootPluginExtension.java | 317 ------------------ .../boot/gradle/plugin/SpringBootPlugin.java | 4 +- 3 files changed, 71 insertions(+), 319 deletions(-) create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/SpringBootExtension.java delete mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/SpringBootPluginExtension.java diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/SpringBootExtension.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/SpringBootExtension.java new file mode 100644 index 00000000000..e534317b2e9 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/SpringBootExtension.java @@ -0,0 +1,69 @@ +/* + * Copyright 2012-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.boot.gradle; + +import org.gradle.api.Action; +import org.gradle.api.Project; +import org.gradle.api.plugins.JavaPlugin; + +import org.springframework.boot.gradle.buildinfo.BuildInfo; + +/** + * Gradle DSL extension that provides the entry point to Spring Boot's DSL. + * + * @author Andy Wilkinson + */ +public class SpringBootExtension { + + private final Project project; + + /** + * Creates a new {@code SpringBootPluginExtension} that is associated with the given + * {@code project}. + * + * @param project the project + */ + public SpringBootExtension(Project project) { + this.project = project; + } + + /** + * Creates a new {@link BuildInfo} task named {@code bootBuildInfo} and configures the + * {@code classes} task to depend upon it. + */ + public void buildInfo() { + this.buildInfo(null); + } + + /** + * Creates a new {@link BuildInfo} task named {@code bootBuildInfo} and configures the + * {@code classes} task to depend upon it. The task is passed to the given + * {@code configurer} for further configuration. + * + * @param configurer the task configurer + */ + public void buildInfo(Action configurer) { + BuildInfo bootBuildInfo = this.project.getTasks().create("bootBuildInfo", + BuildInfo.class); + this.project.getTasks().getByName(JavaPlugin.CLASSES_TASK_NAME) + .dependsOn(bootBuildInfo); + if (configurer != null) { + configurer.execute(bootBuildInfo); + } + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/SpringBootPluginExtension.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/SpringBootPluginExtension.java deleted file mode 100644 index d3fde163f9e..00000000000 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/SpringBootPluginExtension.java +++ /dev/null @@ -1,317 +0,0 @@ -/* - * Copyright 2012-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.boot.gradle; - -import java.io.File; -import java.util.Map; -import java.util.Set; - -import groovy.lang.Closure; -import org.gradle.api.Project; -import org.gradle.api.plugins.JavaPlugin; - -import org.springframework.boot.gradle.buildinfo.BuildInfo; -import org.springframework.boot.loader.tools.Layout; -import org.springframework.boot.loader.tools.LayoutFactory; -import org.springframework.boot.loader.tools.Layouts; - -/** - * Gradle DSL Extension for 'Spring Boot'. Most of the time Spring Boot can guess the - * settings in this extension, but occasionally you might need to explicitly set one or - * two of them. E.g. - * - *
- *     apply plugin: 'org.springframework.boot'
- *     springBoot {
- *         mainClass = 'org.demo.Application'
- *         layout = 'ZIP'
- *     }
- * 
- * - * @author Phillip Webb - * @author Dave Syer - * @author Stephane Nicoll - * @author Andy Wilkinson - */ -public class SpringBootPluginExtension { - - private final Project project; - - /** - * The main class that should be run. Instead of setting this explicitly you can use - * the 'mainClassName' of the project or the 'main' of the 'run' task. If not - * specified the value from the MANIFEST will be used, or if no manifest entry is the - * archive will be searched for a suitable class. - */ - String mainClass; - - /** - * The classifier (file name part before the extension). Instead of setting this - * explicitly you can use the 'classifier' property of the 'bootRepackage' task. If - * not specified the archive will be replaced instead of renamed. - */ - String classifier; - - /** - * The name of the ivy configuration name to treat as 'provided' (when packaging those - * dependencies in a separate path). If not specified 'providedRuntime' will be used. - */ - String providedConfiguration; - - /** - * The name of the custom configuration to use. - */ - String customConfiguration; - - /** - * If the original source archive should be backed-up before being repackaged. - */ - boolean backupSource = true; - - /** - * The layout of the archive if it can't be derived from the file extension. Valid - * values are JAR, WAR, ZIP, DIR (for exploded zip file). ZIP and DIR are actually - * synonymous, and should be used if there is no MANIFEST.MF available, or if you want - * the MANIFEST.MF 'Main-Class' to be PropertiesLauncher. Gradle will coerce literal - * String values to the correct type. - */ - LayoutType layout; - - /** - * The layout factory that will be used when no explicit layout is specified. - * Alternative layouts can be provided by 3rd parties. - */ - LayoutFactory layoutFactory; - - /** - * Libraries that must be unpacked from fat jars in order to run. Use Strings in the - * form {@literal groupId:artifactId}. - */ - Set requiresUnpack; - - /** - * Whether Spring Boot Devtools should be excluded from the fat jar. - */ - boolean excludeDevtools = true; - - /** - * Location of an agent jar to attach to the VM when running the application with - * runJar task. - */ - File agent; - - /** - * Flag to indicate that the agent requires -noverify (and the plugin will refuse to - * start if it is not set). - */ - Boolean noverify; - - /** - * If exclude rules should be applied to dependencies based on the - * spring-dependencies-bom. - */ - boolean applyExcludeRules = true; - - /** - * If a fully executable jar (for *nix machines) should be generated by prepending a - * launch script to the jar. - */ - boolean executable = false; - - /** - * The embedded launch script to prepend to the front of the jar if it is fully - * executable. If not specified the 'Spring Boot' default script will be used. - */ - File embeddedLaunchScript; - - /** - * Properties that should be expanded in the embedded launch script. - */ - Map embeddedLaunchScriptProperties; - - public SpringBootPluginExtension(Project project) { - this.project = project; - } - - /** - * Convenience method for use in a custom task. - * @return the Layout to use or null if not explicitly set - */ - public Layout convertLayout() { - return (this.layout == null ? null : this.layout.layout); - } - - public String getMainClass() { - return this.mainClass; - } - - public void setMainClass(String mainClass) { - this.mainClass = mainClass; - } - - public String getClassifier() { - return this.classifier; - } - - public void setClassifier(String classifier) { - this.classifier = classifier; - } - - public String getProvidedConfiguration() { - return this.providedConfiguration; - } - - public void setProvidedConfiguration(String providedConfiguration) { - this.providedConfiguration = providedConfiguration; - } - - public String getCustomConfiguration() { - return this.customConfiguration; - } - - public void setCustomConfiguration(String customConfiguration) { - this.customConfiguration = customConfiguration; - } - - public boolean isBackupSource() { - return this.backupSource; - } - - public void setBackupSource(boolean backupSource) { - this.backupSource = backupSource; - } - - public LayoutType getLayout() { - return this.layout; - } - - public void setLayout(LayoutType layout) { - this.layout = layout; - } - - public LayoutFactory getLayoutFactory() { - return this.layoutFactory; - } - - public void setLayoutFactory(LayoutFactory layoutFactory) { - this.layoutFactory = layoutFactory; - } - - public Set getRequiresUnpack() { - return this.requiresUnpack; - } - - public void setRequiresUnpack(Set requiresUnpack) { - this.requiresUnpack = requiresUnpack; - } - - public boolean isExcludeDevtools() { - return this.excludeDevtools; - } - - public void setExcludeDevtools(boolean excludeDevtools) { - this.excludeDevtools = excludeDevtools; - } - - public File getAgent() { - return this.agent; - } - - public void setAgent(File agent) { - this.agent = agent; - } - - public Boolean getNoverify() { - return this.noverify; - } - - public void setNoverify(Boolean noverify) { - this.noverify = noverify; - } - - public boolean isApplyExcludeRules() { - return this.applyExcludeRules; - } - - public void setApplyExcludeRules(boolean applyExcludeRules) { - this.applyExcludeRules = applyExcludeRules; - } - - public boolean isExecutable() { - return this.executable; - } - - public void setExecutable(boolean executable) { - this.executable = executable; - } - - public File getEmbeddedLaunchScript() { - return this.embeddedLaunchScript; - } - - public void setEmbeddedLaunchScript(File embeddedLaunchScript) { - this.embeddedLaunchScript = embeddedLaunchScript; - } - - public Map getEmbeddedLaunchScriptProperties() { - return this.embeddedLaunchScriptProperties; - } - - public void setEmbeddedLaunchScriptProperties( - Map embeddedLaunchScriptProperties) { - this.embeddedLaunchScriptProperties = embeddedLaunchScriptProperties; - } - - public void buildInfo() { - this.buildInfo(null); - } - - public void buildInfo(Closure taskConfigurer) { - BuildInfo bootBuildInfo = this.project.getTasks().create("bootBuildInfo", - BuildInfo.class); - this.project.getTasks().getByName(JavaPlugin.CLASSES_TASK_NAME) - .dependsOn(bootBuildInfo); - if (taskConfigurer != null) { - taskConfigurer.setDelegate(bootBuildInfo); - taskConfigurer.call(); - } - } - - /** - * Layout Types. - */ - enum LayoutType { - - JAR(new Layouts.Jar()), - - WAR(new Layouts.War()), - - ZIP(new Layouts.Expanded()), - - DIR(new Layouts.Expanded()), - - NONE(new Layouts.None()); - - Layout layout; - - LayoutType(Layout layout) { - this.layout = layout; - } - - } - -} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java index 67efd9754cb..8bf5ef615fa 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java @@ -22,7 +22,7 @@ import org.gradle.api.Project; import org.gradle.api.Task; import org.gradle.api.tasks.compile.JavaCompile; -import org.springframework.boot.gradle.SpringBootPluginExtension; +import org.springframework.boot.gradle.SpringBootExtension; import org.springframework.boot.gradle.application.ApplicationPluginFeatures; import org.springframework.boot.gradle.bundling.BundlingPluginFeatures; import org.springframework.boot.gradle.dependencymanagement.DependencyManagementPluginFeatures; @@ -39,7 +39,7 @@ public class SpringBootPlugin implements Plugin { @Override public void apply(Project project) { - project.getExtensions().create("springBoot", SpringBootPluginExtension.class, + project.getExtensions().create("springBoot", SpringBootExtension.class, project); new ApplicationPluginFeatures().apply(project); new BundlingPluginFeatures().apply(project); From 188c9e0f423b00029928c8b83759c38a4ebb80d6 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 28 Mar 2017 14:04:44 +0100 Subject: [PATCH 17/30] Remove assumptions from BootInfo and move them to the DSL extension --- .../boot/gradle/SpringBootExtension.java | 43 +++++- .../boot/gradle/buildinfo/BuildInfo.java | 54 ++++---- .../BuildInfoDslIntegrationTests.java | 128 ++++++++++++++++++ .../buildinfo/BuildInfoIntegrationTests.java | 88 ++++++++++++ ...tegrationTests-additionalProperties.gradle | 17 +++ ...ildInfoDslIntegrationTests-basicJar.gradle | 15 ++ ...ildInfoDslIntegrationTests-basicWar.gradle | 15 ++ ...lIntegrationTests-classesDependency.gradle | 12 ++ ...lIntegrationTests-jarWithCustomName.gradle | 19 +++ ...lIntegrationTests-warWithCustomName.gradle | 19 +++ .../BuildInfoIntegrationTests.gradle | 18 +++ 11 files changed, 394 insertions(+), 34 deletions(-) create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/buildinfo/BuildInfoIntegrationTests.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-additionalProperties.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-basicJar.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-basicWar.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-classesDependency.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-jarWithCustomName.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-warWithCustomName.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoIntegrationTests.gradle diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/SpringBootExtension.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/SpringBootExtension.java index e534317b2e9..ae0a73d3ac0 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/SpringBootExtension.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/SpringBootExtension.java @@ -16,9 +16,15 @@ package org.springframework.boot.gradle; +import java.io.File; +import java.util.concurrent.Callable; + import org.gradle.api.Action; import org.gradle.api.Project; import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.plugins.JavaPluginConvention; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.bundling.Jar; import org.springframework.boot.gradle.buildinfo.BuildInfo; @@ -43,7 +49,11 @@ public class SpringBootExtension { /** * Creates a new {@link BuildInfo} task named {@code bootBuildInfo} and configures the - * {@code classes} task to depend upon it. + * Java plugin's {@code classes} task to depend upon it. + *

+ * By default, the task's destination dir will be a directory named {@code META-INF} + * beneath the main source set's resources output directory, and the task's project + * artifact will be the base name of the {@code bootWar} or {@code bootJar} task. */ public void buildInfo() { this.buildInfo(null); @@ -51,16 +61,39 @@ public class SpringBootExtension { /** * Creates a new {@link BuildInfo} task named {@code bootBuildInfo} and configures the - * {@code classes} task to depend upon it. The task is passed to the given - * {@code configurer} for further configuration. + * Java plugin's {@code classes} task to depend upon it. The task is passed to the + * given {@code configurer} for further configuration. + *

+ * By default, the task's destination dir will be a directory named {@code META-INF} + * beneath the main source set's resources output directory, and the task's project + * artifact will be the base name of the {@code bootWar} or {@code bootJar} task. * * @param configurer the task configurer */ public void buildInfo(Action configurer) { BuildInfo bootBuildInfo = this.project.getTasks().create("bootBuildInfo", BuildInfo.class); - this.project.getTasks().getByName(JavaPlugin.CLASSES_TASK_NAME) - .dependsOn(bootBuildInfo); + this.project.getPlugins().withType(JavaPlugin.class, (plugin) -> { + this.project.getTasks().getByName(JavaPlugin.CLASSES_TASK_NAME) + .dependsOn(bootBuildInfo); + bootBuildInfo.getConventionMapping().map("projectArtifact", + (Callable) () -> { + Jar artifactTask = (Jar) this.project.getTasks().findByName("bootWar"); + if (artifactTask == null) { + artifactTask = (Jar) this.project.getTasks().findByName("bootJar"); + } + String result = artifactTask == null ? null : artifactTask.getBaseName(); + return result; + }); + bootBuildInfo.getConventionMapping().map("destinationDir", + (Callable) () -> { + return new File( + this.project.getConvention().getPlugin(JavaPluginConvention.class) + .getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME) + .getOutput().getResourcesDir(), + "META-INF"); + }); + }); if (configurer != null) { configurer.execute(bootBuildInfo); } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/buildinfo/BuildInfo.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/buildinfo/BuildInfo.java index b7d30287f07..c88797ce611 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/buildinfo/BuildInfo.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/buildinfo/BuildInfo.java @@ -22,62 +22,53 @@ import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; -import org.gradle.api.DefaultTask; -import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.Task; +import org.gradle.api.internal.ConventionTask; import org.gradle.api.tasks.Input; -import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.OutputDirectory; import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.TaskExecutionException; -import org.gradle.api.tasks.bundling.Jar; import org.springframework.boot.loader.tools.BuildPropertiesWriter; import org.springframework.boot.loader.tools.BuildPropertiesWriter.ProjectDetails; /** - * {@link DefaultTask} for generating a {@code build-info.properties} file from a - * {@code Project}. - *

- * By default, the {@code build-info.properties} file is generated in - * project.buildDir/resources/main/META-INF. - *

+ * {@link Task} for generating a {@code build-info.properties} file from a + * {@code Project}. The {@link #setDestinationDir destination dir} and + * {@link #setProjectArtifact project artifact} must be configured before execution. * * @author Andy Wilkinson */ -public class BuildInfo extends DefaultTask { +public class BuildInfo extends ConventionTask { - @OutputFile - private File outputFile = getProject().file(new File(getProject().getBuildDir(), - "resources/main/META-INF/build-info.properties")); + private File destinationDir; - @Input private String projectGroup = getProject().getGroup().toString(); - @Input - private String projectArtifact = ((Jar) getProject().getTasks() - .getByName(JavaPlugin.JAR_TASK_NAME)).getBaseName(); + private String projectArtifact; - @Input private String projectVersion = getProject().getVersion().toString(); - @Input private String projectName = getProject().getName(); - @Input private Map additionalProperties = new HashMap<>(); @TaskAction public void generateBuildProperties() { try { - new BuildPropertiesWriter(this.outputFile) - .writeBuildProperties(new ProjectDetails(this.projectGroup, - this.projectArtifact, this.projectVersion, this.projectName, - coerceToStringValues(this.additionalProperties))); + new BuildPropertiesWriter( + new File(getDestinationDir(), "build-info.properties")) + .writeBuildProperties(new ProjectDetails(this.projectGroup, + getProjectArtifact(), this.projectVersion, + this.projectName, + coerceToStringValues(this.additionalProperties))); } catch (IOException ex) { throw new TaskExecutionException(this, ex); } } + @Input public String getProjectGroup() { return this.projectGroup; } @@ -86,6 +77,7 @@ public class BuildInfo extends DefaultTask { this.projectGroup = projectGroup; } + @Input public String getProjectArtifact() { return this.projectArtifact; } @@ -94,6 +86,7 @@ public class BuildInfo extends DefaultTask { this.projectArtifact = projectArtifact; } + @Input public String getProjectVersion() { return this.projectVersion; } @@ -102,6 +95,7 @@ public class BuildInfo extends DefaultTask { this.projectVersion = projectVersion; } + @Input public String getProjectName() { return this.projectName; } @@ -110,14 +104,16 @@ public class BuildInfo extends DefaultTask { this.projectName = projectName; } - public File getOutputFile() { - return this.outputFile; + @OutputDirectory + public File getDestinationDir() { + return this.destinationDir; } - public void setOutputFile(File outputFile) { - this.outputFile = outputFile; + public void setDestinationDir(File destinationDir) { + this.destinationDir = destinationDir; } + @Input public Map getAdditionalProperties() { return this.additionalProperties; } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests.java new file mode 100644 index 00000000000..01fd1a622fa --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests.java @@ -0,0 +1,128 @@ +/* + * Copyright 2012-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.boot.gradle.buildinfo; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.Properties; + +import org.gradle.testkit.runner.TaskOutcome; +import org.junit.Rule; +import org.junit.Test; + +import org.springframework.boot.gradle.testkit.GradleBuild; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for {@link BuildInfo} created using the + * {@link org.springframework.boot.gradle.SpringBootExtension DSL}. + * + * @author Andy Wilkinson + */ +public class BuildInfoDslIntegrationTests { + + @Rule + public final GradleBuild gradleBuild = new GradleBuild(); + + @Test + public void basicJar() throws IOException { + assertThat(this.gradleBuild.build("bootBuildInfo", "--stacktrace") + .task(":bootBuildInfo").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + Properties properties = buildInfoProperties(); + assertThat(properties).containsEntry("build.name", + this.gradleBuild.getProjectDir().getName()); + assertThat(properties).containsEntry("build.artifact", + this.gradleBuild.getProjectDir().getName()); + assertThat(properties).containsEntry("build.group", "com.example"); + assertThat(properties).containsEntry("build.version", "1.0"); + } + + @Test + public void jarWithCustomName() throws IOException { + assertThat(this.gradleBuild.build("bootBuildInfo", "--stacktrace") + .task(":bootBuildInfo").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + Properties properties = buildInfoProperties(); + assertThat(properties).containsEntry("build.name", + this.gradleBuild.getProjectDir().getName()); + assertThat(properties).containsEntry("build.artifact", "foo"); + assertThat(properties).containsEntry("build.group", "com.example"); + assertThat(properties).containsEntry("build.version", "1.0"); + } + + @Test + public void basicWar() throws IOException { + assertThat(this.gradleBuild.build("bootBuildInfo", "--stacktrace") + .task(":bootBuildInfo").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + Properties properties = buildInfoProperties(); + assertThat(properties).containsEntry("build.name", + this.gradleBuild.getProjectDir().getName()); + assertThat(properties).containsEntry("build.artifact", + this.gradleBuild.getProjectDir().getName()); + assertThat(properties).containsEntry("build.group", "com.example"); + assertThat(properties).containsEntry("build.version", "1.0"); + } + + @Test + public void warWithCustomName() throws IOException { + assertThat(this.gradleBuild.build("bootBuildInfo", "--stacktrace") + .task(":bootBuildInfo").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + Properties properties = buildInfoProperties(); + assertThat(properties).containsEntry("build.name", + this.gradleBuild.getProjectDir().getName()); + assertThat(properties).containsEntry("build.artifact", "foo"); + assertThat(properties).containsEntry("build.group", "com.example"); + assertThat(properties).containsEntry("build.version", "1.0"); + } + + @Test + public void additionalProperties() throws IOException { + assertThat(this.gradleBuild.build("bootBuildInfo", "--stacktrace") + .task(":bootBuildInfo").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + Properties properties = buildInfoProperties(); + assertThat(properties).containsEntry("build.name", + this.gradleBuild.getProjectDir().getName()); + assertThat(properties).containsEntry("build.artifact", + this.gradleBuild.getProjectDir().getName()); + assertThat(properties).containsEntry("build.group", "com.example"); + assertThat(properties).containsEntry("build.version", "1.0"); + assertThat(properties).containsEntry("build.a", "alpha"); + assertThat(properties).containsEntry("build.b", "bravo"); + } + + @Test + public void classesDependency() throws IOException { + assertThat(this.gradleBuild.build("classes", "--stacktrace") + .task(":bootBuildInfo").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + } + + private Properties buildInfoProperties() { + File file = new File(this.gradleBuild.getProjectDir(), + "build/resources/main/META-INF/build-info.properties"); + assertThat(file).isFile(); + Properties properties = new Properties(); + try (FileReader reader = new FileReader(file)) { + properties.load(reader); + return properties; + } + catch (IOException ex) { + throw new RuntimeException(ex); + } + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/buildinfo/BuildInfoIntegrationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/buildinfo/BuildInfoIntegrationTests.java new file mode 100644 index 00000000000..ddeac1bb973 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/buildinfo/BuildInfoIntegrationTests.java @@ -0,0 +1,88 @@ +/* + * Copyright 2012-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.boot.gradle.buildinfo; + +import org.gradle.testkit.runner.TaskOutcome; +import org.junit.Rule; +import org.junit.Test; + +import org.springframework.boot.gradle.testkit.GradleBuild; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for {@link BuildInfo}. + * + * @author Andy Wilkinson + */ +public class BuildInfoIntegrationTests { + + @Rule + public final GradleBuild gradleBuild = new GradleBuild(); + + @Test + public void basicExecution() { + assertThat(this.gradleBuild.build("buildInfo").task(":buildInfo").getOutcome()) + .isEqualTo(TaskOutcome.SUCCESS); + } + + @Test + public void upToDateWhenExecutedTwice() { + assertThat(this.gradleBuild.build("buildInfo").task(":buildInfo").getOutcome()) + .isEqualTo(TaskOutcome.SUCCESS); + assertThat(this.gradleBuild.build("buildInfo").task(":buildInfo").getOutcome()) + .isEqualTo(TaskOutcome.UP_TO_DATE); + } + + @Test + public void notUpToDateWhenDestinationDirChanges() { + notUpToDateWithChangeToProperty("buildInfoDestinationDir"); + } + + @Test + public void notUpToDateWhenProjectArtifactChanges() { + notUpToDateWithChangeToProperty("buildInfoProjectArtifact"); + } + + @Test + public void notUpToDateWhenProjectGroupChanges() { + notUpToDateWithChangeToProperty("buildInfoProjectGroup"); + } + + @Test + public void notUpToDateWhenProjectVersionChanges() { + notUpToDateWithChangeToProperty("buildInfoProjectVersion"); + } + + @Test + public void notUpToDateWhenProjectNameChanges() { + notUpToDateWithChangeToProperty("buildInfoProjectName"); + } + + @Test + public void notUpToDateWhenAdditionalPropertyChanges() { + notUpToDateWithChangeToProperty("buildInfoAdditionalProperty"); + } + + private void notUpToDateWithChangeToProperty(String name) { + assertThat(this.gradleBuild.build("buildInfo").task(":buildInfo").getOutcome()) + .isEqualTo(TaskOutcome.SUCCESS); + assertThat(this.gradleBuild.build("buildInfo", "-P" + name + "=changed") + .task(":buildInfo").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-additionalProperties.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-additionalProperties.gradle new file mode 100644 index 00000000000..93162070be5 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-additionalProperties.gradle @@ -0,0 +1,17 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'java' + +group = 'com.example' +version = '1.0' + +springBoot { + buildInfo { + additionalProperties 'a': 'alpha', 'b': 'bravo' + } +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-basicJar.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-basicJar.gradle new file mode 100644 index 00000000000..92cd7455635 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-basicJar.gradle @@ -0,0 +1,15 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'java' + +group = 'com.example' +version = '1.0' + +springBoot { + buildInfo() +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-basicWar.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-basicWar.gradle new file mode 100644 index 00000000000..6a627257c26 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-basicWar.gradle @@ -0,0 +1,15 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'war' + +group = 'com.example' +version = '1.0' + +springBoot { + buildInfo() +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-classesDependency.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-classesDependency.gradle new file mode 100644 index 00000000000..e217894ff20 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-classesDependency.gradle @@ -0,0 +1,12 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'java' + +springBoot { + buildInfo() +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-jarWithCustomName.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-jarWithCustomName.gradle new file mode 100644 index 00000000000..adb3428ee08 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-jarWithCustomName.gradle @@ -0,0 +1,19 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'java' + +group = 'com.example' +version = '1.0' + +bootJar { + baseName = 'foo' +} + +springBoot { + buildInfo() +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-warWithCustomName.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-warWithCustomName.gradle new file mode 100644 index 00000000000..e679477c158 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-warWithCustomName.gradle @@ -0,0 +1,19 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'war' + +group = 'com.example' +version = '1.0' + +bootWar { + baseName = 'foo' +} + +springBoot { + buildInfo() +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoIntegrationTests.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoIntegrationTests.gradle new file mode 100644 index 00000000000..fe973b6bee5 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoIntegrationTests.gradle @@ -0,0 +1,18 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +def property(String name, Object defaultValue) { + project.hasProperty(name) ? project.getProperty(name) : defaultValue +} + +task buildInfo(type: org.springframework.boot.gradle.buildinfo.BuildInfo) { + destinationDir file(property('buildInfoDestinationDir', project.buildDir)) + projectArtifact property('buildInfoProjectArtifact', 'foo') + projectVersion property('buildInfoProjectVersion', '1.0') + projectGroup property('buildInfoProjectGroup', 'foo') + projectName property('buildInfoProjectName', 'foo') + additionalProperties 'additional': property('buildInfoAdditionalProperty', 'foo') +} From 1dc5c484f0969ae9d6a6d6a03e97525c20a53770 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 28 Mar 2017 14:41:12 +0100 Subject: [PATCH 18/30] Use application plugin properties by convention when it is applied bootRun's main and jvmArgs properties use mainClassName and applicationDefaultJvmArgs respectively by convention. bootJar and bootWar's mainClass property uses mainClassName by convention. --- .../bundling/BundlingPluginFeatures.java | 21 +++++++++++++------ .../boot/gradle/run/RunPluginFeatures.java | 21 +++++++++---------- .../AbstractBootArchiveIntegrationTests.java | 15 +++++++++++++ .../gradle/run/BootRunIntegrationTests.java | 18 ++++++++++++++++ ...pplicationPluginMainClassNameIsUsed.gradle | 11 ++++++++++ ...pplicationPluginMainClassNameIsUsed.gradle | 11 ++++++++++ ...pplicationPluginJvmArgumentsAreUsed.gradle | 14 +++++++++++++ ...pplicationPluginMainClassNameIsUsed.gradle | 14 +++++++++++++ 8 files changed, 108 insertions(+), 17 deletions(-) create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/BootJarIntegrationTests-applicationPluginMainClassNameIsUsed.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/BootWarIntegrationTests-applicationPluginMainClassNameIsUsed.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/run/BootRunIntegrationTests-applicationPluginJvmArgumentsAreUsed.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/run/BootRunIntegrationTests-applicationPluginMainClassNameIsUsed.gradle diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BundlingPluginFeatures.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BundlingPluginFeatures.java index 5baa4a9cbcf..b1a262e61f6 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BundlingPluginFeatures.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BundlingPluginFeatures.java @@ -19,6 +19,7 @@ package org.springframework.boot.gradle.bundling; import java.util.Collections; import java.util.Set; import java.util.concurrent.Callable; +import java.util.function.Supplier; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; @@ -66,9 +67,8 @@ public class BundlingPluginFeatures implements PluginFeatures { ArchivePublishArtifact artifact = new ArchivePublishArtifact(bootWar); this.singlePublishedArtifact.addCandidate(artifact); project.getComponents().add(new BootSoftwareComponent(artifact, "bootWeb")); - bootWar.conventionMapping("mainClass", () -> { - return new MainClassResolver(bootWar.getClasspath()).resolveMainClass(); - }); + bootWar.conventionMapping("mainClass", + mainClassConvention(project, bootWar::getClasspath)); } private void configureBootJarTask(Project project) { @@ -83,9 +83,18 @@ public class BundlingPluginFeatures implements PluginFeatures { ArchivePublishArtifact artifact = new ArchivePublishArtifact(bootJar); this.singlePublishedArtifact.addCandidate(artifact); project.getComponents().add(new BootSoftwareComponent(artifact, "bootJava")); - bootJar.conventionMapping("mainClass", () -> { - return new MainClassResolver(bootJar.getClasspath()).resolveMainClass(); - }); + bootJar.conventionMapping("mainClass", + mainClassConvention(project, bootJar::getClasspath)); + } + + private Callable mainClassConvention(Project project, + Supplier classpathSupplier) { + return () -> { + if (project.hasProperty("mainClassName")) { + return project.property("mainClassName"); + } + return new MainClassResolver(classpathSupplier.get()).resolveMainClass(); + }; } private void configureBootArchivesUpload(Project project) { diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/RunPluginFeatures.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/RunPluginFeatures.java index 0ad7034b4f7..c7838b22a45 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/RunPluginFeatures.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/RunPluginFeatures.java @@ -44,26 +44,25 @@ public class RunPluginFeatures implements PluginFeatures { }); } - private void addBootRunTask(final Project project) { - final JavaPluginConvention javaConvention = project.getConvention() + private void addBootRunTask(Project project) { + JavaPluginConvention javaConvention = project.getConvention() .getPlugin(JavaPluginConvention.class); - BootRun run = project.getTasks().create(RUN_APP_TASK_NAME, BootRun.class); run.setDescription("Run the project with support for " + "auto-detecting main class and reloading static resources"); run.setGroup("application"); run.classpath(javaConvention.getSourceSets() .findByName(SourceSet.MAIN_SOURCE_SET_NAME).getRuntimeClasspath()); - run.getConventionMapping().map("jvmArgs", new Callable() { - @Override - public Object call() throws Exception { - if (project.hasProperty("applicationDefaultJvmArgs")) { - return project.property("applicationDefaultJvmArgs"); - } - return Collections.emptyList(); + run.getConventionMapping().map("jvmArgs", ((Callable) () -> { + if (project.hasProperty("applicationDefaultJvmArgs")) { + return project.property("applicationDefaultJvmArgs"); } - }); + return Collections.emptyList(); + })); run.conventionMapping("main", () -> { + if (project.hasProperty("mainClassName")) { + return project.property("mainClassName"); + } return new MainClassResolver(run.getClasspath()).resolveMainClass(); }); } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveIntegrationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveIntegrationTests.java index 9ab3e301c93..11ad775d9d2 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveIntegrationTests.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveIntegrationTests.java @@ -16,7 +16,9 @@ package org.springframework.boot.gradle.bundling; +import java.io.File; import java.io.IOException; +import java.util.jar.JarFile; import org.gradle.testkit.runner.InvalidRunnerConfigurationException; import org.gradle.testkit.runner.TaskOutcome; @@ -97,4 +99,17 @@ public abstract class AbstractBootArchiveIntegrationTests { .getOutcome()).isEqualTo(TaskOutcome.SUCCESS); } + @Test + public void applicationPluginMainClassNameIsUsed() throws IOException { + assertThat(this.gradleBuild.build(this.taskName).task(":" + this.taskName) + .getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + try (JarFile jarFile = new JarFile( + new File(this.gradleBuild.getProjectDir(), "build/libs") + .listFiles()[0])) { + assertThat(jarFile.getManifest().getMainAttributes().getValue("Start-Class")) + .isEqualTo("com.example.CustomMain"); + } + + } + } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/run/BootRunIntegrationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/run/BootRunIntegrationTests.java index 5945b21551f..120a211be7c 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/run/BootRunIntegrationTests.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/run/BootRunIntegrationTests.java @@ -66,6 +66,24 @@ public class BootRunIntegrationTests { assertThat(result.getOutput()).doesNotContain(urlOf("build/resources/main")); } + @Test + public void applicationPluginMainClassNameIsUsed() throws IOException { + BuildResult result = this.gradleBuild.build("echoMainClassName"); + assertThat(result.task(":echoMainClassName").getOutcome()) + .isEqualTo(TaskOutcome.UP_TO_DATE); + assertThat(result.getOutput()) + .contains("Main class name = com.example.CustomMainClass"); + } + + @Test + public void applicationPluginJvmArgumentsAreUsed() throws IOException { + BuildResult result = this.gradleBuild.build("echoJvmArguments"); + assertThat(result.task(":echoJvmArguments").getOutcome()) + .isEqualTo(TaskOutcome.UP_TO_DATE); + assertThat(result.getOutput()) + .contains("JVM arguments = [-Dcom.foo=bar, -Dcom.bar=baz]"); + } + private String urlOf(String path) throws IOException { return new File(this.gradleBuild.getProjectDir().getCanonicalFile(), path).toURI() .toURL().toString(); diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/BootJarIntegrationTests-applicationPluginMainClassNameIsUsed.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/BootJarIntegrationTests-applicationPluginMainClassNameIsUsed.gradle new file mode 100644 index 00000000000..684171c1403 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/BootJarIntegrationTests-applicationPluginMainClassNameIsUsed.gradle @@ -0,0 +1,11 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'java' +apply plugin: 'org.springframework.boot' +apply plugin: 'application' + +mainClassName = 'com.example.CustomMain' diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/BootWarIntegrationTests-applicationPluginMainClassNameIsUsed.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/BootWarIntegrationTests-applicationPluginMainClassNameIsUsed.gradle new file mode 100644 index 00000000000..8da00e317bf --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/BootWarIntegrationTests-applicationPluginMainClassNameIsUsed.gradle @@ -0,0 +1,11 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'war' +apply plugin: 'org.springframework.boot' +apply plugin: 'application' + +mainClassName = 'com.example.CustomMain' diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/run/BootRunIntegrationTests-applicationPluginJvmArgumentsAreUsed.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/run/BootRunIntegrationTests-applicationPluginJvmArgumentsAreUsed.gradle new file mode 100644 index 00000000000..96047897b24 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/run/BootRunIntegrationTests-applicationPluginJvmArgumentsAreUsed.gradle @@ -0,0 +1,14 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'application' +apply plugin: 'org.springframework.boot' + +applicationDefaultJvmArgs = ['-Dcom.foo=bar', '-Dcom.bar=baz'] + +task echoJvmArguments { + println 'JVM arguments = ' + bootRun.jvmArgs +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/run/BootRunIntegrationTests-applicationPluginMainClassNameIsUsed.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/run/BootRunIntegrationTests-applicationPluginMainClassNameIsUsed.gradle new file mode 100644 index 00000000000..a9163cd7d3e --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/run/BootRunIntegrationTests-applicationPluginMainClassNameIsUsed.gradle @@ -0,0 +1,14 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'application' +apply plugin: 'org.springframework.boot' + +mainClassName = 'com.example.CustomMainClass' + +task echoMainClassName { + println 'Main class name = ' + bootRun.main +} From 5f27ecc6d9b52829a0190cdd71c04ad3d6ab883a Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 28 Mar 2017 15:17:54 +0100 Subject: [PATCH 19/30] Remove old integration tests for the Gradle plugin --- spring-boot-integration-tests/pom.xml | 1 - .../spring-boot-gradle-tests/pom.xml | 87 -------- .../boot/gradle/BootRunResourceTests.java | 66 ------ .../boot/gradle/ClassifierTests.java | 62 ------ .../boot/gradle/FlatdirTests.java | 60 ----- .../boot/gradle/FullyExecutableJarTests.java | 182 --------------- .../boot/gradle/InstallTests.java | 47 ---- .../boot/gradle/MainClassTests.java | 72 ------ .../gradle/MixedVersionRepackagingTests.java | 84 ------- .../gradle/MultiProjectRepackagingTests.java | 78 ------- .../boot/gradle/NoJarTests.java | 45 ---- .../boot/gradle/ProjectCreator.java | 66 ------ .../boot/gradle/RepackagingTests.java | 210 ------------------ .../boot/gradle/SpringLoadedTests.java | 95 -------- .../springframework/boot/gradle/Versions.java | 66 ------ .../boot/gradle/WarPackagingTests.java | 181 --------------- .../StarterDependenciesIntegrationTests.java | 114 ---------- .../resources/boot-run-resources/build.gradle | 19 -- .../java/BootRunResourcesApplication.java | 24 -- .../src/main/resources/test.txt | 0 .../resources/classifier-extension.gradle | 30 --- .../src/test/resources/classifier.gradle | 30 --- .../resources/executable-jar/build.gradle | 53 ----- .../resources/executable-jar/extension.script | 1 - .../test/resources/executable-jar/task.script | 1 - .../src/test/resources/flatdir.gradle | 30 --- .../src/test/resources/foo.jar | Bin 2533 -> 0 bytes .../src/test/resources/install-app.gradle | 33 --- .../src/test/resources/installer.gradle | 40 ---- .../src/test/resources/main-class.gradle | 39 ---- .../mixed-version-repackaging.gradle | 30 --- .../build.gradle | 32 --- .../lib/foo.jar | Bin 2533 -> 0 bytes .../settings.gradle | 3 - .../build.gradle | 25 --- .../settings.gradle | 1 - .../build.gradle | 40 ---- .../common/lib/foo.jar | Bin 2533 -> 0 bytes .../settings.gradle | 3 - .../src/test/resources/nojar.gradle | 30 --- .../src/test/resources/repackage.gradle | 66 ------ .../spring-loaded-jvm-args/build.gradle | 31 --- .../src/main/java/test/Application.java | 41 ---- .../spring-loaded-old-gradle/build.gradle | 26 --- .../src/main/java/test/Application.java | 41 ---- .../resources/starter-dependencies.gradle | 60 ----- .../src/test/resources/war-packaging.gradle | 27 --- 47 files changed, 2272 deletions(-) delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/pom.xml delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/BootRunResourceTests.java delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/ClassifierTests.java delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/FlatdirTests.java delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/FullyExecutableJarTests.java delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/InstallTests.java delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/MainClassTests.java delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/MixedVersionRepackagingTests.java delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/MultiProjectRepackagingTests.java delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/NoJarTests.java delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/ProjectCreator.java delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/RepackagingTests.java delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/SpringLoadedTests.java delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/Versions.java delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/WarPackagingTests.java delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/starter/StarterDependenciesIntegrationTests.java delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/boot-run-resources/build.gradle delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/boot-run-resources/src/main/java/BootRunResourcesApplication.java delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/boot-run-resources/src/main/resources/test.txt delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/classifier-extension.gradle delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/classifier.gradle delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/executable-jar/build.gradle delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/executable-jar/extension.script delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/executable-jar/task.script delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/flatdir.gradle delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/foo.jar delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/install-app.gradle delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/installer.gradle delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/main-class.gradle delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/mixed-version-repackaging.gradle delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/multi-project-common-file-dependency/build.gradle delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/multi-project-common-file-dependency/lib/foo.jar delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/multi-project-common-file-dependency/settings.gradle delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/multi-project-runtime-project-dependency/build.gradle delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/multi-project-runtime-project-dependency/settings.gradle delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/multi-project-transitive-file-dependency/build.gradle delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/multi-project-transitive-file-dependency/common/lib/foo.jar delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/multi-project-transitive-file-dependency/settings.gradle delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/nojar.gradle delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/repackage.gradle delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/spring-loaded-jvm-args/build.gradle delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/spring-loaded-jvm-args/src/main/java/test/Application.java delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/spring-loaded-old-gradle/build.gradle delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/spring-loaded-old-gradle/src/main/java/test/Application.java delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/starter-dependencies.gradle delete mode 100644 spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/war-packaging.gradle diff --git a/spring-boot-integration-tests/pom.xml b/spring-boot-integration-tests/pom.xml index 6b62d0d12f6..dc500531375 100644 --- a/spring-boot-integration-tests/pom.xml +++ b/spring-boot-integration-tests/pom.xml @@ -23,7 +23,6 @@ spring-boot-devtools-tests spring-boot-integration-tests-embedded-servlet-container - spring-boot-gradle-tests spring-boot-launch-script-tests spring-boot-security-tests diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/pom.xml b/spring-boot-integration-tests/spring-boot-gradle-tests/pom.xml deleted file mode 100644 index 7f465d6f398..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/pom.xml +++ /dev/null @@ -1,87 +0,0 @@ - - - 4.0.0 - - org.springframework.boot - spring-boot-integration-tests - 2.0.0.BUILD-SNAPSHOT - - spring-boot-gradle-tests - jar - Spring Boot Gradle Integration Tests - Spring Boot Gradle Integration Tests - http://projects.spring.io/spring-boot/ - - Pivotal Software, Inc. - http://www.spring.io - - - ${basedir}/../.. - - - - org.gradle - gradle-tooling-api - ${gradle.version} - test - - - org.springframework.boot - spring-boot - test - - - org.apache.maven - maven-model-builder - 3.2.1 - test - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - copy-effective-pom - generate-test-resources - - copy - - - - - org.springframework.boot - spring-boot-dependencies - ${project.version} - effective-pom - true - ${project.build.directory} - dependencies-pom.xml - - - - - - - - - - - gradle - http://repo.gradle.org/gradle/libs-releases-local - - true - - - false - - - - diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/BootRunResourceTests.java b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/BootRunResourceTests.java deleted file mode 100644 index 68c701f4aff..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/BootRunResourceTests.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2012-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.boot.gradle; - -import java.io.IOException; - -import org.gradle.tooling.ProjectConnection; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; - -import org.springframework.boot.test.rule.OutputCapture; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Integration tests for bootRun's resource handling - * - * @author Andy Wilkinson - */ -public class BootRunResourceTests { - - private static final String BOOT_VERSION = Versions.getBootVersion(); - - private static ProjectConnection project; - - @Rule - public OutputCapture output = new OutputCapture(); - - @BeforeClass - public static void createProject() throws IOException { - project = new ProjectCreator().createProject("boot-run-resources"); - } - - @Test - public void resourcesDirectlyFromSource() { - project.newBuild().forTasks("clean", "bootRun") - .withArguments("-PbootVersion=" + BOOT_VERSION, "-PaddResources=true") - .setStandardOutput(System.out).run(); - - assertThat(this.output.toString()).contains("src/main/resources/test.txt"); - } - - @Test - public void resourcesFromBuildOutput() { - project.newBuild().forTasks("clean", "bootRun") - .withArguments("-PbootVersion=" + BOOT_VERSION, "-PaddResources=false") - .setStandardOutput(System.out).run(); - assertThat(this.output.toString()).contains("build/resources/main/test.txt"); - } - -} diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/ClassifierTests.java b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/ClassifierTests.java deleted file mode 100644 index 37a52642b59..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/ClassifierTests.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2012-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.boot.gradle; - -import java.util.jar.JarFile; - -import org.gradle.tooling.ProjectConnection; -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for using the Gradle plugin's support for installing artifacts - * - * @author Dave Syer - */ -public class ClassifierTests { - - private ProjectConnection project; - - private static final String BOOT_VERSION = Versions.getBootVersion(); - - @Test - public void classifierInBootTask() throws Exception { - this.project = new ProjectCreator().createProject("classifier"); - this.project.newBuild().forTasks("build") - .withArguments("-PbootVersion=" + BOOT_VERSION, "--stacktrace").run(); - checkFilesExist("classifier"); - } - - @Test - public void classifierInBootExtension() throws Exception { - this.project = new ProjectCreator().createProject("classifier-extension"); - this.project.newBuild().forTasks("build") - .withArguments("-PbootVersion=" + BOOT_VERSION, "--stacktrace", "--info") - .run(); - } - - private void checkFilesExist(String name) throws Exception { - JarFile jar = new JarFile("target/" + name + "/build/libs/" + name + ".jar"); - assertThat(jar.getManifest()).isNotNull(); - jar.close(); - jar = new JarFile("target/" + name + "/build/libs/" + name + "-exec.jar"); - assertThat(jar.getManifest()).isNotNull(); - jar.close(); - } - -} diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/FlatdirTests.java b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/FlatdirTests.java deleted file mode 100644 index 15da226fb50..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/FlatdirTests.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2012-2015 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.boot.gradle; - -import java.io.File; - -import org.gradle.tooling.ProjectConnection; -import org.junit.Before; -import org.junit.Test; - -import org.springframework.util.FileCopyUtils; -import org.springframework.util.FileSystemUtils; - -/** - * Tests for using the Gradle plugin's support for flat directory repos - * - * @author Dave Syer - */ -public class FlatdirTests { - - private ProjectConnection project; - - private File libs = new File("target/flatdir/lib"); - - private static final String BOOT_VERSION = Versions.getBootVersion(); - - @Before - public void init() { - if (this.libs.exists()) { - FileSystemUtils.deleteRecursively(this.libs); - } - } - - @Test - public void flatdir() throws Exception { - this.project = new ProjectCreator().createProject("flatdir"); - if (!this.libs.exists()) { - this.libs.mkdirs(); - } - FileCopyUtils.copy(new File("src/test/resources/foo.jar"), - new File(this.libs, "foo-1.0.0.jar")); - this.project.newBuild().forTasks("build") - .withArguments("-PbootVersion=" + BOOT_VERSION, "--stacktrace").run(); - } - -} diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/FullyExecutableJarTests.java b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/FullyExecutableJarTests.java deleted file mode 100644 index c90f1fc40dd..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/FullyExecutableJarTests.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright 2012-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.boot.gradle; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Pattern; - -import org.gradle.tooling.ProjectConnection; -import org.junit.BeforeClass; -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Integration tests for creating a fully executable jar with Gradle. - * - * @author Andy Wilkinson - */ -public class FullyExecutableJarTests { - - private static final String BOOT_VERSION = Versions.getBootVersion(); - - private static ProjectConnection project; - - @BeforeClass - public static void createProject() throws IOException { - project = new ProjectCreator().createProject("executable-jar"); - } - - @Test - public void jarIsNotExecutableByDefault() throws IOException { - project.newBuild().forTasks("clean", "build") - .withArguments("-PbootVersion=" + BOOT_VERSION).run(); - File buildLibs = new File("target/executable-jar/build/libs"); - File executableJar = new File(buildLibs, "executable-jar.jar"); - assertThat(isFullyExecutable(executableJar)).isFalse(); - } - - @Test - public void madeExecutableViaExtension() throws IOException { - project.newBuild().forTasks("clean", "build").withArguments( - "-PbootVersion=" + BOOT_VERSION, "-PextensionExecutable=true").run(); - File buildLibs = new File("target/executable-jar/build/libs"); - File executableJar = new File(buildLibs, "executable-jar.jar"); - assertThat(isFullyExecutable(executableJar)).isTrue(); - } - - @Test - public void madeExecutableViaTask() throws IOException { - project.newBuild().forTasks("clean", "build") - .withArguments("-PbootVersion=" + BOOT_VERSION, "-PtaskExecutable=true") - .run(); - File buildLibs = new File("target/executable-jar/build/libs"); - File executableJar = new File(buildLibs, "executable-jar.jar"); - assertThat(isFullyExecutable(executableJar)).isTrue(); - } - - @Test - public void taskTakesPrecedenceForMakingJarExecutable() throws IOException { - project.newBuild().forTasks("clean", "build") - .withArguments("-PbootVersion=" + BOOT_VERSION, - "-PextensionExecutable=false", "-PtaskExecutable=true") - .run(); - File buildLibs = new File("target/executable-jar/build/libs"); - File executableJar = new File(buildLibs, "executable-jar.jar"); - assertThat(isFullyExecutable(executableJar)).isTrue(); - } - - @Test - public void scriptPropertiesFromTask() throws IOException { - project.newBuild().forTasks("clean", "build") - .withArguments("-PbootVersion=" + BOOT_VERSION, "-PtaskProperties=true") - .run(); - File buildLibs = new File("target/executable-jar/build/libs"); - File executableJar = new File(buildLibs, "executable-jar.jar"); - assertThat(isFullyExecutable(executableJar)).isTrue(); - assertThat(containsLine("# Provides:.*__task__", executableJar)).isTrue(); - } - - @Test - public void scriptPropertiesFromExtension() throws IOException { - project.newBuild().forTasks("clean", "build").withArguments( - "-PbootVersion=" + BOOT_VERSION, "-PextensionProperties=true").run(); - File buildLibs = new File("target/executable-jar/build/libs"); - File executableJar = new File(buildLibs, "executable-jar.jar"); - assertThat(isFullyExecutable(executableJar)).isTrue(); - assertThat(containsLine("# Provides:.*__extension__", executableJar)).isTrue(); - } - - @Test - public void taskTakesPrecedenceForScriptProperties() throws IOException { - project.newBuild().forTasks("clean", "build") - .withArguments("-PbootVersion=" + BOOT_VERSION, - "-PextensionProperties=true", "-PtaskProperties=true") - .run(); - File buildLibs = new File("target/executable-jar/build/libs"); - File executableJar = new File(buildLibs, "executable-jar.jar"); - assertThat(isFullyExecutable(executableJar)).isTrue(); - assertThat(containsLine("# Provides:.*__task__", executableJar)).isTrue(); - } - - @Test - public void customScriptFromTask() throws IOException { - project.newBuild().forTasks("clean", "build") - .withArguments("-PbootVersion=" + BOOT_VERSION, "-PtaskScript=true") - .run(); - File buildLibs = new File("target/executable-jar/build/libs"); - File executableJar = new File(buildLibs, "executable-jar.jar"); - assertThat(containsLine("Custom task script", executableJar)).isTrue(); - } - - @Test - public void customScriptFromExtension() throws IOException { - project.newBuild().forTasks("clean", "build") - .withArguments("-PbootVersion=" + BOOT_VERSION, "-PextensionScript=true") - .run(); - File buildLibs = new File("target/executable-jar/build/libs"); - File executableJar = new File(buildLibs, "executable-jar.jar"); - assertThat(containsLine("Custom extension script", executableJar)).isTrue(); - } - - @Test - public void taskTakesPrecedenceForCustomScript() throws IOException { - project.newBuild().forTasks("clean", "build") - .withArguments("-PbootVersion=" + BOOT_VERSION, "-PextensionScript=true", - "-PtaskScript=true") - .run(); - File buildLibs = new File("target/executable-jar/build/libs"); - File executableJar = new File(buildLibs, "executable-jar.jar"); - assertThat(containsLine("Custom task script", executableJar)).isTrue(); - } - - private boolean isFullyExecutable(File file) throws IOException { - return containsLine("#!/bin/bash", file); - } - - private boolean containsLine(String toMatch, File file) throws IOException { - Pattern pattern = Pattern.compile(toMatch); - for (String line : readLines(file)) { - if (pattern.matcher(line).matches()) { - return true; - } - } - return false; - } - - private List readLines(File file) throws IOException { - BufferedReader reader = new BufferedReader(new FileReader(file)); - List lines = new ArrayList<>(); - try { - String line = reader.readLine(); - while (line != null && lines.size() < 50) { - lines.add(line); - line = reader.readLine(); - } - } - finally { - reader.close(); - } - return lines; - } - -} diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/InstallTests.java b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/InstallTests.java deleted file mode 100644 index 344edb79221..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/InstallTests.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2012-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.boot.gradle; - -import org.gradle.tooling.ProjectConnection; -import org.junit.Test; - -/** - * Tests for using the Gradle plugin's support for installing artifacts - * - * @author Dave Syer - */ -public class InstallTests { - - private ProjectConnection project; - - private static final String BOOT_VERSION = Versions.getBootVersion(); - - @Test - public void cleanInstall() throws Exception { - this.project = new ProjectCreator().createProject("installer"); - this.project.newBuild().forTasks("install") - .withArguments("-PbootVersion=" + BOOT_VERSION, "--stacktrace").run(); - } - - @Test - public void cleanInstallDist() throws Exception { - this.project = new ProjectCreator().createProject("install-app"); - this.project.newBuild().forTasks("installDist") - .withArguments("-PbootVersion=" + BOOT_VERSION, "--stacktrace").run(); - } - -} diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/MainClassTests.java b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/MainClassTests.java deleted file mode 100644 index 2921466c1b5..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/MainClassTests.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2012-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.boot.gradle; - -import java.io.IOException; - -import org.gradle.tooling.BuildException; -import org.gradle.tooling.ProjectConnection; -import org.junit.BeforeClass; -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for configuring a project's main class. - * - * @author Dave Syer - * @author Andy Wilkinson - */ -public class MainClassTests { - - private static final String BOOT_VERSION = Versions.getBootVersion(); - - private static ProjectConnection project; - - @BeforeClass - public static void createProject() throws IOException { - project = new ProjectCreator().createProject("main-class"); - } - - @Test - public void mainFromBootRun() { - project.newBuild().forTasks("build") - .withArguments("-PbootVersion=" + BOOT_VERSION, "-PbootRunMain=true") - .run(); - } - - @Test - public void nonJavaExecRunTaskIsIgnored() { - try { - project.newBuild().forTasks("build").withArguments( - "-PbootVersion=" + BOOT_VERSION, "-PnonJavaExecRun=true").run(); - } - catch (BuildException ex) { - Throwable rootCause = getRootCause(ex); - assertThat(rootCause.getMessage()).isEqualTo("Unable to find main class"); - } - } - - private Throwable getRootCause(Throwable ex) { - Throwable candidate = ex; - while (candidate.getCause() != null) { - candidate = candidate.getCause(); - } - return candidate; - } - -} diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/MixedVersionRepackagingTests.java b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/MixedVersionRepackagingTests.java deleted file mode 100644 index f4d0832f509..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/MixedVersionRepackagingTests.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2012-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.boot.gradle; - -import java.io.File; -import java.io.IOException; -import java.util.jar.JarFile; - -import org.assertj.core.api.Condition; -import org.assertj.core.description.TextDescription; -import org.gradle.tooling.ProjectConnection; -import org.junit.BeforeClass; -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.not; - -/** - * Integration tests for Gradle repackaging with two different versions of the same - * dependency. - * - * @author Andy Wilkinson - */ -public class MixedVersionRepackagingTests { - - private static final String BOOT_VERSION = Versions.getBootVersion(); - - private static ProjectConnection project; - - @BeforeClass - public static void createProject() throws IOException { - project = new ProjectCreator().createProject("mixed-version-repackaging"); - } - - @Test - public void singleVersionIsIncludedInJar() throws IOException { - project.newBuild().forTasks("clean", "build") - .withArguments("-PbootVersion=" + BOOT_VERSION, "-Prepackage=true", - "-PexcludeDevtools=false") - .run(); - File buildLibs = new File("target/mixed-version-repackaging/build/libs"); - File repackageFile = new File(buildLibs, "mixed-version-repackaging.jar"); - assertThat(repackageFile.exists()).isTrue(); - assertThat(new JarFile(repackageFile)) - .has(entryNamed("BOOT-INF/lib/guava-18.0.jar")); - assertThat(new JarFile(repackageFile)) - .has(not(entryNamed("BOOT-INF/lib/guava-16.0.jar"))); - } - - private Condition entryNamed(String name) { - return new JarFileEntryCondition(name); - } - - private final class JarFileEntryCondition extends Condition { - - private final String entryName; - - private JarFileEntryCondition(String entryName) { - super(new TextDescription("entry named '%s'", entryName)); - this.entryName = entryName; - } - - @Override - public boolean matches(JarFile jarFile) { - return jarFile.getEntry(this.entryName) != null; - } - - } - -} diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/MultiProjectRepackagingTests.java b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/MultiProjectRepackagingTests.java deleted file mode 100644 index b8296184e34..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/MultiProjectRepackagingTests.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2012-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.boot.gradle; - -import java.io.File; -import java.util.jar.JarFile; - -import org.gradle.tooling.ProjectConnection; -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Integration tests for Gradle repackaging with a multi-project build. - * - * @author Andy Wilkinson - */ -public class MultiProjectRepackagingTests { - - private static final String BOOT_VERSION = Versions.getBootVersion(); - - @Test - public void repackageWithTransitiveFileDependency() throws Exception { - ProjectConnection project = new ProjectCreator() - .createProject("multi-project-transitive-file-dependency"); - project.newBuild().forTasks("clean", "build") - .withArguments("-PbootVersion=" + BOOT_VERSION).run(); - File buildLibs = new File( - "target/multi-project-transitive-file-dependency/main/build/libs"); - JarFile jarFile = new JarFile(new File(buildLibs, "main.jar")); - assertThat(jarFile.getEntry("BOOT-INF/lib/commons-logging-1.1.3.jar")) - .isNotNull(); - assertThat(jarFile.getEntry("BOOT-INF/lib/foo.jar")).isNotNull(); - jarFile.close(); - } - - @Test - public void repackageWithCommonFileDependency() throws Exception { - ProjectConnection project = new ProjectCreator() - .createProject("multi-project-common-file-dependency"); - project.newBuild().forTasks("clean", "build") - .withArguments("-PbootVersion=" + BOOT_VERSION).run(); - File buildLibs = new File( - "target/multi-project-common-file-dependency/build/libs"); - JarFile jarFile = new JarFile( - new File(buildLibs, "multi-project-common-file-dependency.jar")); - assertThat(jarFile.getEntry("BOOT-INF/lib/foo.jar")).isNotNull(); - jarFile.close(); - } - - @Test - public void repackageWithRuntimeProjectDependency() throws Exception { - ProjectConnection project = new ProjectCreator() - .createProject("multi-project-runtime-project-dependency"); - project.newBuild().forTasks("clean", "build") - .withArguments("-PbootVersion=" + BOOT_VERSION).run(); - File buildLibs = new File( - "target/multi-project-runtime-project-dependency/projectA/build/libs"); - JarFile jarFile = new JarFile(new File(buildLibs, "projectA.jar")); - assertThat(jarFile.getEntry("BOOT-INF/lib/projectB.jar")).isNotNull(); - jarFile.close(); - } - -} diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/NoJarTests.java b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/NoJarTests.java deleted file mode 100644 index d3051eda25a..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/NoJarTests.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2012-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.boot.gradle; - -import java.io.File; - -import org.gradle.tooling.ProjectConnection; -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for using the Gradle plugin's support for flat directory repos - * - * @author Dave Syer - */ -public class NoJarTests { - - private ProjectConnection project; - - private static final String BOOT_VERSION = Versions.getBootVersion(); - - @Test - public void nojar() throws Exception { - this.project = new ProjectCreator().createProject("nojar"); - this.project.newBuild().forTasks("build") - .withArguments("-PbootVersion=" + BOOT_VERSION, "--stacktrace").run(); - assertThat(new File("target/nojar/build/libs")).doesNotExist(); - } - -} diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/ProjectCreator.java b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/ProjectCreator.java deleted file mode 100644 index 8e08921d372..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/ProjectCreator.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2012-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.boot.gradle; - -import java.io.File; -import java.io.IOException; - -import org.gradle.tooling.GradleConnector; -import org.gradle.tooling.ProjectConnection; -import org.gradle.tooling.internal.consumer.DefaultGradleConnector; - -import org.springframework.util.FileCopyUtils; -import org.springframework.util.FileSystemUtils; - -/** - * @author Andy Wilkinson - */ -public class ProjectCreator { - - private String gradleVersion; - - public ProjectCreator() { - this("3.4"); - } - - public ProjectCreator(String gradleVersion) { - this.gradleVersion = gradleVersion; - } - - public ProjectConnection createProject(String name) throws IOException { - File projectDirectory = new File("target/" + name); - projectDirectory.mkdirs(); - - File gradleScript = new File(projectDirectory, "build.gradle"); - - if (new File("src/test/resources", name).isDirectory()) { - FileSystemUtils.copyRecursively(new File("src/test/resources", name), - projectDirectory); - } - else { - FileCopyUtils.copy(new File("src/test/resources/" + name + ".gradle"), - gradleScript); - } - - GradleConnector gradleConnector = GradleConnector.newConnector(); - gradleConnector.useGradleVersion(this.gradleVersion); - - ((DefaultGradleConnector) gradleConnector).embedded(true); - return gradleConnector.forProjectDirectory(projectDirectory).connect(); - } - -} diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/RepackagingTests.java b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/RepackagingTests.java deleted file mode 100644 index fd4b7e1e548..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/RepackagingTests.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright 2012-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.boot.gradle; - -import java.io.File; -import java.io.IOException; -import java.util.jar.JarFile; - -import org.gradle.tooling.BuildLauncher; -import org.gradle.tooling.ProjectConnection; -import org.junit.BeforeClass; -import org.junit.Test; - -import org.springframework.util.FileCopyUtils; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Integration tests for gradle repackaging. - * - * @author Andy Wilkinson - */ -public class RepackagingTests { - - private static final String BOOT_VERSION = Versions.getBootVersion(); - - private static ProjectConnection project; - - @BeforeClass - public static void createProject() throws IOException { - project = new ProjectCreator().createProject("repackage"); - } - - @Test - public void repackagingEnabled() throws IOException { - createBuildForTasks("clean", "build") - .withArguments("-PbootVersion=" + BOOT_VERSION, "-Prepackage=true").run(); - File buildLibs = new File("target/repackage/build/libs"); - File repackageFile = new File(buildLibs, "repackage.jar"); - assertThat(repackageFile.exists()).isTrue(); - assertThat(new File(buildLibs, "repackage.jar.original").exists()).isTrue(); - assertThat(new File(buildLibs, "repackage-sources.jar.original").exists()) - .isFalse(); - } - - @Test - public void repackagingDisabled() { - createBuildForTasks("clean", "build") - .withArguments("-PbootVersion=" + BOOT_VERSION, "-Prepackage=false") - .run(); - File buildLibs = new File("target/repackage/build/libs"); - assertThat(new File(buildLibs, "repackage.jar").exists()).isTrue(); - assertThat(new File(buildLibs, "repackage.jar.original").exists()).isFalse(); - assertThat(new File(buildLibs, "repackage-sources.jar.original").exists()) - .isFalse(); - } - - @Test - public void repackagingDisabledWithCustomRepackagedJar() { - createBuildForTasks("clean", "build", "customRepackagedJar") - .withArguments("-PbootVersion=" + BOOT_VERSION, "-Prepackage=false") - .run(); - File buildLibs = new File("target/repackage/build/libs"); - assertThat(new File(buildLibs, "repackage.jar").exists()).isTrue(); - assertThat(new File(buildLibs, "repackage.jar.original").exists()).isFalse(); - assertThat(new File(buildLibs, "repackage-sources.jar.original").exists()) - .isFalse(); - assertThat(new File(buildLibs, "custom.jar").exists()).isTrue(); - assertThat(new File(buildLibs, "custom.jar.original").exists()).isTrue(); - } - - @Test - public void repackagingDisabledWithCustomRepackagedJarUsingStringJarTaskReference() { - project.newBuild() - .forTasks("clean", "build", "customRepackagedJarWithStringReference") - .withArguments("-PbootVersion=" + BOOT_VERSION, "-Prepackage=false") - .run(); - File buildLibs = new File("target/repackage/build/libs"); - assertThat(new File(buildLibs, "repackage.jar").exists()).isTrue(); - assertThat(new File(buildLibs, "repackage.jar.original").exists()).isFalse(); - assertThat(new File(buildLibs, "repackage-sources.jar.original").exists()) - .isFalse(); - assertThat(new File(buildLibs, "custom.jar").exists()).isTrue(); - assertThat(new File(buildLibs, "custom.jar.original").exists()).isTrue(); - } - - @Test - public void repackagingEnabledWithCustomRepackagedJar() { - createBuildForTasks("clean", "build", "customRepackagedJar") - .withArguments("-PbootVersion=" + BOOT_VERSION, "-Prepackage=true").run(); - File buildLibs = new File("target/repackage/build/libs"); - assertThat(new File(buildLibs, "repackage.jar").exists()).isTrue(); - assertThat(new File(buildLibs, "repackage.jar.original").exists()).isTrue(); - assertThat(new File(buildLibs, "repackage-sources.jar.original").exists()) - .isFalse(); - assertThat(new File(buildLibs, "custom.jar").exists()).isTrue(); - assertThat(new File(buildLibs, "custom.jar.original").exists()).isTrue(); - } - - @Test - public void repackagingEnableWithCustomRepackagedJarUsingStringJarTaskReference() { - project.newBuild() - .forTasks("clean", "build", "customRepackagedJarWithStringReference") - .withArguments("-PbootVersion=" + BOOT_VERSION, "-Prepackage=true").run(); - File buildLibs = new File("target/repackage/build/libs"); - assertThat(new File(buildLibs, "repackage.jar").exists()).isTrue(); - assertThat(new File(buildLibs, "repackage.jar.original").exists()).isTrue(); - assertThat(new File(buildLibs, "repackage-sources.jar.original").exists()) - .isFalse(); - assertThat(new File(buildLibs, "custom.jar").exists()).isTrue(); - assertThat(new File(buildLibs, "custom.jar.original").exists()).isTrue(); - } - - @Test - public void repackageWithFileDependency() throws Exception { - FileCopyUtils.copy(new File("src/test/resources/foo.jar"), - new File("target/repackage/foo.jar")); - createBuildForTasks("clean", "build") - .withArguments("-PbootVersion=" + BOOT_VERSION, "-Prepackage=true").run(); - File buildLibs = new File("target/repackage/build/libs"); - JarFile jarFile = new JarFile(new File(buildLibs, "repackage.jar")); - assertThat(jarFile.getEntry("BOOT-INF/lib/foo.jar")).isNotNull(); - jarFile.close(); - } - - @Test - public void devtoolsIsExcludedByDefault() throws IOException { - createBuildForTasks("clean", "bootRepackage") - .withArguments("-PbootVersion=" + BOOT_VERSION, "-Prepackage=true").run(); - File buildLibs = new File("target/repackage/build/libs"); - File repackageFile = new File(buildLibs, "repackage.jar"); - assertThat(repackageFile.exists()).isTrue(); - assertThat(new File(buildLibs, "repackage.jar.original").exists()).isTrue(); - assertThat(new File(buildLibs, "repackage-sources.jar.original").exists()) - .isFalse(); - assertThat(isDevToolsJarIncluded(repackageFile)).isFalse(); - } - - @Test - public void devtoolsCanBeIncludedUsingTheExtension() throws IOException { - createBuildForTasks("clean", "bootRepackage") - .withArguments("-PbootVersion=" + BOOT_VERSION, "-Prepackage=true", - "-PexcludeDevtoolsOnExtension=false") - .run(); - File buildLibs = new File("target/repackage/build/libs"); - File repackageFile = new File(buildLibs, "repackage.jar"); - assertThat(repackageFile.exists()).isTrue(); - assertThat(new File(buildLibs, "repackage.jar.original").exists()).isTrue(); - assertThat(new File(buildLibs, "repackage-sources.jar.original").exists()) - .isFalse(); - assertThat(isDevToolsJarIncluded(repackageFile)).isTrue(); - } - - @Test - public void devtoolsCanBeIncludedUsingBootRepackage() throws IOException { - createBuildForTasks("clean", "bootRepackage") - .withArguments("-PbootVersion=" + BOOT_VERSION, "-Prepackage=true", - "-PexcludeDevtoolsOnBootRepackage=false") - .run(); - File buildLibs = new File("target/repackage/build/libs"); - File repackageFile = new File(buildLibs, "repackage.jar"); - assertThat(repackageFile.exists()).isTrue(); - assertThat(new File(buildLibs, "repackage.jar.original").exists()).isTrue(); - assertThat(new File(buildLibs, "repackage-sources.jar.original").exists()) - .isFalse(); - assertThat(isDevToolsJarIncluded(repackageFile)).isTrue(); - } - - @Test - public void customRepackagingTaskWithOwnMainClassNameAnNoGlobalMainClassName() { - createBuildForTasks("clean", "customRepackagedJarWithOwnMainClass") - .withArguments("-PbootVersion=" + BOOT_VERSION, "-Prepackage=true", - "-PnoMainClass=true") - .run(); - File buildLibs = new File("target/repackage/build/libs"); - assertThat(new File(buildLibs, "custom.jar").exists()).isTrue(); - assertThat(new File(buildLibs, "custom.jar.original").exists()).isTrue(); - } - - private BuildLauncher createBuildForTasks(String... taskNames) { - return project.newBuild().setStandardError(System.err) - .setStandardOutput(System.out).forTasks(taskNames); - } - - private boolean isDevToolsJarIncluded(File repackageFile) throws IOException { - JarFile jarFile = new JarFile(repackageFile); - try { - String name = "BOOT-INF/lib/spring-boot-devtools-" + BOOT_VERSION + ".jar"; - return jarFile.getEntry(name) != null; - } - finally { - jarFile.close(); - } - } - -} diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/SpringLoadedTests.java b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/SpringLoadedTests.java deleted file mode 100644 index da131ea2bec..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/SpringLoadedTests.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2012-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.boot.gradle; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.gradle.tooling.ProjectConnection; -import org.junit.Test; - -import static org.junit.Assert.fail; - -/** - * Integration tests for the Gradle plugin's Spring Loaded support - * - * @author Andy Wilkinson - */ -public class SpringLoadedTests { - - private static final String BOOT_VERSION = Versions.getBootVersion(); - - private static final String SPRING_LOADED_VERSION = Versions.getSpringLoadedVersion(); - - @Test - public void defaultJvmArgsArePreservedWhenLoadedAgentIsConfigured() - throws IOException { - ProjectConnection project = new ProjectCreator() - .createProject("spring-loaded-jvm-args"); - project.newBuild().forTasks("bootRun") - .withArguments("-PbootVersion=" + BOOT_VERSION, - "-PspringLoadedVersion=" + SPRING_LOADED_VERSION, "--stacktrace") - .run(); - - List output = getOutput(); - assertOutputContains("-DSOME_ARG=someValue", output); - assertOutputContains("-Xverify:none", output); - assertOutputMatches("-javaagent:.*springloaded-" + SPRING_LOADED_VERSION + ".jar", - output); - } - - private List getOutput() throws IOException { - BufferedReader reader = new BufferedReader(new FileReader( - new File("target/spring-loaded-jvm-args/build/output.txt"))); - try { - List lines = new ArrayList<>(); - - String line; - - while ((line = reader.readLine()) != null) { - lines.add(line); - } - return lines; - } - finally { - reader.close(); - } - } - - private void assertOutputContains(String requiredOutput, List actualOutput) { - for (String line : actualOutput) { - if (line.equals(requiredOutput)) { - return; - } - } - fail("Required output '" + requiredOutput + "' not found in " + actualOutput); - } - - private void assertOutputMatches(String requiredPattern, List actualOutput) { - for (String line : actualOutput) { - if (line.matches(requiredPattern)) { - return; - } - } - fail("Required pattern '" + requiredPattern + "' not matched in " + actualOutput); - } - -} diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/Versions.java b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/Versions.java deleted file mode 100644 index 63431bd9426..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/Versions.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2012-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.boot.gradle; - -import java.io.FileReader; - -import javax.xml.xpath.XPath; -import javax.xml.xpath.XPathExpression; -import javax.xml.xpath.XPathFactory; - -import org.xml.sax.InputSource; - -/** - * @author Andy Wilkinson - */ -public final class Versions { - - private Versions() { - } - - public static String getBootVersion() { - return evaluateExpression( - "/*[local-name()='project']/*[local-name()='version']" + "/text()"); - } - - public static String getSpringLoadedVersion() { - return evaluateExpression( - "/*[local-name()='project']/*[local-name()='properties']" - + "/*[local-name()='spring-loaded.version']/text()"); - } - - public static String getSpringVersion() { - return evaluateExpression( - "/*[local-name()='project']/*[local-name()='properties']" - + "/*[local-name()='spring.version']/text()"); - } - - private static String evaluateExpression(String expression) { - try { - XPathFactory xPathFactory = XPathFactory.newInstance(); - XPath xpath = xPathFactory.newXPath(); - XPathExpression expr = xpath.compile(expression); - String version = expr.evaluate( - new InputSource(new FileReader("target/dependencies-pom.xml"))); - return version; - } - catch (Exception ex) { - throw new IllegalStateException("Failed to evaluate expression", ex); - } - } - -} diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/WarPackagingTests.java b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/WarPackagingTests.java deleted file mode 100644 index 3c5b41c429b..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/WarPackagingTests.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright 2012-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.boot.gradle; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Enumeration; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; - -import org.gradle.tooling.ProjectConnection; -import org.junit.BeforeClass; -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for war packaging with Gradle to ensure that only the Servlet container and its - * dependencies are packaged in WEB-INF/lib-provided - * - * @author Andy Wilkinson - */ -public class WarPackagingTests { - - private static final String WEB_INF_LIB_PROVIDED_PREFIX = "WEB-INF/lib-provided/"; - - private static final String WEB_INF_LIB_PREFIX = "WEB-INF/lib/"; - - private static final Set TOMCAT_EXPECTED_IN_WEB_INF_LIB_PROVIDED = new HashSet<>( - Arrays.asList("spring-boot-starter-tomcat-", "tomcat-embed-core-", - "tomcat-embed-el-", "tomcat-embed-websocket-")); - - private static final Set JETTY_EXPECTED_IN_WEB_INF_LIB_PROVIDED = new HashSet<>( - Arrays.asList("spring-boot-starter-jetty-", "jetty-continuation", - "jetty-util-", "javax.servlet-", "jetty-client", "jetty-io-", - "jetty-http-", "jetty-server-", "jetty-security-", "jetty-servlet-", - "jetty-servlets", "jetty-webapp-", "websocket-api", - "javax.annotation-api", "jetty-plus", "javax-websocket-server-impl-", - "apache-el", "asm-", "javax.websocket-api-", "asm-tree-", - "asm-commons-", "websocket-common-", "jetty-annotations-", - "javax-websocket-client-impl-", "websocket-client-", - "websocket-server-", "jetty-xml-", "websocket-servlet-")); - - private static final String BOOT_VERSION = Versions.getBootVersion(); - - private static ProjectConnection project; - - @BeforeClass - public static void createProject() throws IOException { - project = new ProjectCreator().createProject("war-packaging"); - } - - @Test - public void onlyTomcatIsPackagedInWebInfLibProvided() throws IOException { - checkWebInfEntriesForWebServer("tomcat", - TOMCAT_EXPECTED_IN_WEB_INF_LIB_PROVIDED); - } - - @Test - public void onlyJettyIsPackagedInWebInfLibProvided() throws IOException { - checkWebInfEntriesForWebServer("jetty", - JETTY_EXPECTED_IN_WEB_INF_LIB_PROVIDED); - } - - private void checkWebInfEntriesForWebServer(String webServer, - Set expectedLibProvidedEntries) throws IOException { - project.newBuild().forTasks("clean", "build") - .withArguments("-PbootVersion=" + BOOT_VERSION, - "-PservletContainer=" + webServer) - .run(); - - JarFile war = new JarFile("target/war-packaging/build/libs/war-packaging.war"); - - checkWebInfLibProvidedEntries(war, expectedLibProvidedEntries); - - checkWebInfLibEntries(war, expectedLibProvidedEntries); - - try { - war.close(); - } - catch (IOException ex) { - // Ignore - } - } - - private void checkWebInfLibProvidedEntries(JarFile war, Set expectedEntries) - throws IOException { - Set entries = getWebInfLibProvidedEntries(war); - assertThat(entries).hasSameSizeAs(expectedEntries); - List unexpectedLibProvidedEntries = new ArrayList<>(); - for (String entry : entries) { - if (!isExpectedInWebInfLibProvided(entry, expectedEntries)) { - unexpectedLibProvidedEntries.add(entry); - } - } - assertThat(unexpectedLibProvidedEntries.isEmpty()); - } - - private void checkWebInfLibEntries(JarFile war, Set entriesOnlyInLibProvided) - throws IOException { - Set entries = getWebInfLibEntries(war); - List unexpectedLibEntries = new ArrayList<>(); - for (String entry : entries) { - if (!isExpectedInWebInfLib(entry, entriesOnlyInLibProvided)) { - unexpectedLibEntries.add(entry); - } - } - assertThat(unexpectedLibEntries.isEmpty()); - } - - private Set getWebInfLibProvidedEntries(JarFile war) throws IOException { - Set webInfLibProvidedEntries = new HashSet<>(); - Enumeration entries = war.entries(); - while (entries.hasMoreElements()) { - String name = entries.nextElement().getName(); - if (isWebInfLibProvidedEntry(name)) { - webInfLibProvidedEntries.add(name); - } - } - return webInfLibProvidedEntries; - } - - private Set getWebInfLibEntries(JarFile war) throws IOException { - Set webInfLibEntries = new HashSet<>(); - Enumeration entries = war.entries(); - while (entries.hasMoreElements()) { - String name = entries.nextElement().getName(); - if (isWebInfLibEntry(name)) { - webInfLibEntries.add(name); - } - } - return webInfLibEntries; - } - - private boolean isWebInfLibProvidedEntry(String name) { - return name.startsWith(WEB_INF_LIB_PROVIDED_PREFIX) - && !name.equals(WEB_INF_LIB_PROVIDED_PREFIX); - } - - private boolean isWebInfLibEntry(String name) { - return name.startsWith(WEB_INF_LIB_PREFIX) && !name.equals(WEB_INF_LIB_PREFIX); - } - - private boolean isExpectedInWebInfLibProvided(String name, - Set expectedEntries) { - for (String expected : expectedEntries) { - if (name.startsWith(WEB_INF_LIB_PROVIDED_PREFIX + expected)) { - return true; - } - } - return false; - } - - private boolean isExpectedInWebInfLib(String name, Set unexpectedEntries) { - for (String unexpected : unexpectedEntries) { - if (name.startsWith(WEB_INF_LIB_PREFIX + unexpected)) { - return false; - } - } - return true; - } - -} diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/starter/StarterDependenciesIntegrationTests.java b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/starter/StarterDependenciesIntegrationTests.java deleted file mode 100644 index f8d2fc7922a..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/starter/StarterDependenciesIntegrationTests.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2012-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.boot.starter; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.gradle.tooling.BuildException; -import org.gradle.tooling.ProjectConnection; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - -import org.springframework.boot.gradle.ProjectCreator; -import org.springframework.boot.gradle.Versions; - -/** - * Tests for the various starter projects to check that they don't pull in unwanted - * transitive dependencies when used with Gradle - * - * @author Andy Wilkinson - */ -@RunWith(Parameterized.class) -public class StarterDependenciesIntegrationTests { - - private static final String STARTER_NAME_PREFIX = "spring-boot-starter"; - - private static final List EXCLUDED_STARTERS = Arrays - .asList("spring-boot-starter-parent"); - - private static ProjectConnection project; - - private static String bootVersion; - - private static String springVersion; - - private final String[] buildArguments; - - @Parameters - public static List getStarters() { - List starters = new ArrayList<>(); - for (File file : new File("../../spring-boot-starters").listFiles()) { - if (file.isDirectory() && new File(file, "pom.xml").exists()) { - String name = file.getName(); - if (name.startsWith(STARTER_NAME_PREFIX) - && !EXCLUDED_STARTERS.contains(file.getName())) { - starters.add(new String[] { file.getName() }); - } - } - } - return starters; - } - - @BeforeClass - public static void createProject() throws IOException { - project = new ProjectCreator().createProject("starter-dependencies"); - } - - @BeforeClass - public static void determineVersions() throws Exception { - springVersion = Versions.getSpringVersion(); - bootVersion = Versions.getBootVersion(); - } - - @AfterClass - public static void closeProject() { - project.close(); - } - - public StarterDependenciesIntegrationTests(String starter) { - this.buildArguments = new String[] { "-Pstarter=" + starter, - "-PbootVersion=" + bootVersion, "-PspringVersion=" + springVersion }; - } - - @Test - public void commonsLoggingIsNotATransitiveDependency() throws IOException { - runBuildForTask("checkCommonsLogging"); - } - - @Test - public void oldSpringModulesAreNotTransitiveDependencies() throws IOException { - runBuildForTask("checkSpring"); - } - - private void runBuildForTask(String task) { - try { - project.newBuild().forTasks(task).withArguments(this.buildArguments).run(); - } - catch (BuildException ex) { - throw new RuntimeException(ex); - } - } - -} diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/boot-run-resources/build.gradle b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/boot-run-resources/build.gradle deleted file mode 100644 index c8a854743cc..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/boot-run-resources/build.gradle +++ /dev/null @@ -1,19 +0,0 @@ -buildscript { - repositories { - mavenLocal() - } - dependencies { - classpath("org.springframework.boot:spring-boot-gradle-plugin:${project.bootVersion}") - } -} - -apply plugin: 'java' -apply plugin: 'org.springframework.boot' - -repositories { - mavenLocal() -} - -bootRun { - addResources = Boolean.valueOf(project.addResources) -} \ No newline at end of file diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/boot-run-resources/src/main/java/BootRunResourcesApplication.java b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/boot-run-resources/src/main/java/BootRunResourcesApplication.java deleted file mode 100644 index 1a640c586ba..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/boot-run-resources/src/main/java/BootRunResourcesApplication.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2012-2015 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. - */ - -public class BootRunResourcesApplication { - - public static void main(String[] args) { - ClassLoader classLoader = BootRunResourcesApplication.class.getClassLoader(); - System.out.println(classLoader.getResource("test.txt")); - } - -} diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/boot-run-resources/src/main/resources/test.txt b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/boot-run-resources/src/main/resources/test.txt deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/classifier-extension.gradle b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/classifier-extension.gradle deleted file mode 100644 index 302cb56e617..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/classifier-extension.gradle +++ /dev/null @@ -1,30 +0,0 @@ -buildscript { - repositories { - mavenLocal() - } - dependencies { - classpath("org.springframework.boot:spring-boot-gradle-plugin:${project.bootVersion}") - } -} - -apply plugin: 'java' -apply plugin: 'maven' -apply plugin: 'org.springframework.boot' - -jar { - baseName = 'classifier-extension' -} - -springBoot { - classifier = 'exec' - mainClass = 'demo.Application' -} - -repositories { - mavenLocal() - mavenCentral() -} - -dependencies { - compile "org.springframework:spring-core" -} diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/classifier.gradle b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/classifier.gradle deleted file mode 100644 index abf9c95bfaa..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/classifier.gradle +++ /dev/null @@ -1,30 +0,0 @@ -buildscript { - repositories { - mavenLocal() - } - dependencies { - classpath("org.springframework.boot:spring-boot-gradle-plugin:${project.bootVersion}") - } -} - -apply plugin: 'java' -apply plugin: 'maven' -apply plugin: 'org.springframework.boot' - -jar { - baseName = 'classifier' -} - -bootRepackage { - classifier = 'exec' - mainClass = 'demo.Application' -} - -repositories { - mavenLocal() - mavenCentral() -} - -dependencies { - compile "org.springframework:spring-core" -} diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/executable-jar/build.gradle b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/executable-jar/build.gradle deleted file mode 100644 index 25986b5968b..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/executable-jar/build.gradle +++ /dev/null @@ -1,53 +0,0 @@ -buildscript { - repositories { - mavenLocal() - } - dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:${project.bootVersion}" - } -} - -repositories { - mavenLocal() - mavenCentral() -} - -apply plugin: 'org.springframework.boot' -apply plugin: 'java' - -dependencies { - compile 'org.springframework.boot:spring-boot-starter-freemarker' - compile 'org.springframework.boot:spring-boot-starter-web' - compile 'org.springframework.boot:spring-boot-devtools' - compile files("foo.jar") -} - -springBoot { - mainClass = 'foo.bar.Baz' -} - -if (project.properties['taskExecutable']) { - bootRepackage.executable = Boolean.valueOf(project.taskExecutable) -} - -if (project.properties['extensionExecutable']) { - springBoot.executable = Boolean.valueOf(project.extensionExecutable) -} - -if (project.properties['taskProperties']) { - bootRepackage.executable = true - bootRepackage.embeddedLaunchScriptProperties = ['initInfoProvides': '__task__'] -} - -if (project.properties['extensionProperties']) { - bootRepackage.executable = true - springBoot.embeddedLaunchScriptProperties = ['initInfoProvides': '__extension__'] -} - -if (project.properties['taskScript']) { - bootRepackage.embeddedLaunchScript = file('task.script') -} - -if (project.properties['extensionScript']) { - springBoot.embeddedLaunchScript = file('extension.script') -} diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/executable-jar/extension.script b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/executable-jar/extension.script deleted file mode 100644 index 9c5657221ef..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/executable-jar/extension.script +++ /dev/null @@ -1 +0,0 @@ -Custom extension script diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/executable-jar/task.script b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/executable-jar/task.script deleted file mode 100644 index 17a55b540d5..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/executable-jar/task.script +++ /dev/null @@ -1 +0,0 @@ -Custom task script diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/flatdir.gradle b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/flatdir.gradle deleted file mode 100644 index 1617ae2a874..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/flatdir.gradle +++ /dev/null @@ -1,30 +0,0 @@ -buildscript { - repositories { - mavenLocal() - } - dependencies { - classpath("org.springframework.boot:spring-boot-gradle-plugin:${project.bootVersion}") - } -} - -apply plugin: 'org.springframework.boot' - -group = 'flatdir' -version = '0.0.0' - -bootRun { - main = 'Foo' -} - -jar { - baseName = 'flatdir' -} - -repositories { - mavenLocal() - flatDir( dirs:'lib' ) -} - -dependencies { - compile ':foo:1.0.0' -} diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/foo.jar b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/foo.jar deleted file mode 100644 index 6b017f90e884d32125c6b332115fc67370c09e18..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2533 zcmah~2{@E%8y*H@OfeWCMTMpTht&XlC$#(gznPcA~ z>l`Y^TBwFBNgPx^C0ldyf77v@QvZ8h@B4n=^*;A~J@0cp?|oxXU^Xrgb4X$lx?evw z7AUZz^~|&t4NZ{Bn2j(_&>r~DJnhL zi&B*9>FTY-stl-(zwuEp>SrDu`nNncraDU=7;NEBZV_ci6?rU6KU^ zvSI^)_5gV~`dxB&cE-CXxs!a0O*>6mRp1TNc5m~bd@&-?iv#=#JTFcRpG*zx9+G=E z_FCF3&peAQsQi@XQdhg1e8u(nq2!LaPU{gFTGd~Ttr#hcw?1Y?6#ADEyZGZKZUs$y zS(e5_{Hd)9EG}y%1pT6*B9Wx%gSp1+o^Sv2#wXS#Bqds?IyO~3C^_TKAf_p|F=1wS zkf?ft%Vs3nIH8v3?ibs8hl?ln#_k`_^2eo%%}98p#GnusoCH{x6M>V;H_YJzc z&Rv^GbJ6thpH#lEuPR)MG_T#KEe!bp#~5+-g^GNLl_MAGE4^QK%xWFdi4?h}Q8}ti z=J|Iu18a|u)&NzU1tzl@ke;4OPVNpQk|MgBn`#eFAG9+c3IwbFvd9t>-&XiaD`*{O zx01UiH5jBU6B`Mc$2eZ}Po!tiJ9<8U3}#6Y_w5rGaw`^1xi+uhAwkpgzELw{9$ZPZ3fy1Ad=zhx;V5UI2OuxlS0Tdsb`DaW!pT z?)y&X0WP=I=|l&AyvJ6*e;@p>e*Y)f7fGMLmpxI3jGJ-v~ct)?zZ7c4UZ{u z8`XcvBxXG${x!p^^8uIiLb7HkA#{0UHGSSOEwA-xVq82USPe;aZTFFL%#@kVQ4ToB zSk8%eh3~@_2@QgK!NDax`UyBef)K&E+vhBo&Ou?mBUJV0q~HkF7X{QzqJZZ@S~mwV zpF;y0arn!zW}KEQGB{Hn?L#w8pCHg`OWCA`FyQ;sje)cC8rQs6<`O@TpVqo4kdRGn zfvNIVF1lreI3MFIfBlD6ECRnE`@5RyiME^Uu(Ow&B+Vjk8(>ugCNg0lFGc`4Ea^#H zJ3K(T$f`TX+mF(`5`3j{B``#A!tcM%m`3}uL#FUoQH2Hv>|p~uqUUrQ=1R-tY%+_2 z+5=qq9G_pZk77Gr>=q~O8s|Z`BedKHZLf zA=&drG?$tYiGE_%Q}I195uYhxqLmSE4yzFlna%}Lx=OAt^8O9yvZ`LYy_k-}g`G3a zMigl3eR}Y;akVaoQEIy?mzCY3M#h(1d88%mWPvE}T(F|`)wLtuU8!-Q%dnqm>z8fl z{BX3c$aNO>V)%Y-v&15<;_fkPBXar8TM!!Y^X)k6%Hq=oLv`HsMfBu+JU&(Ft>)$W z=P?qUpefyDv;LO6De`dS`!X>4rBhJNnq@bJ6m5qnWmywhny-lXt@4$<{!-TzE$s=0 zC#j0f=JLa02fvwV{8;$f`7&`$%5oxQjO$j@xU3pUbRIGz(!=i?_sBy%P<*kUER(0! zYiUXkN~IpZ3M1Nt4hx{2|MYEiY$k>*t4Hv@z^~D)P!=Dtk8}TCx|d7u2_uZCtn)xA z*b1pTZ&MhL3iPEo6K8RcjFIHY@^+nzee_$<0e&3*EL3IyqtA)(1L;Y_lWe(*NjNFaS$Yo?jn~$DJLGEqB6|<3d}Af z#_LuPo(0?w8O@=3Za!~L5Cw1o4y%eRrbD>`Q}+KqBQGCMFT9WMB|OR0|6Dgp(MZ7% zecpg#Hjh?7b-%`P%c9M_6wsC`M=&aCWk=L%SdXMf=!OOBI+aumWKVQ4PF~bK1wI8H zu|M-T%${c*Ub2|Yc*@#~7$y%)@RhV>le?R~z0sgV?0vQ7X z4tl4}+_$u88@9U-GlQ8UH(Ul-w_rP5c_+hej`;+c2Bt1=GZ_AWVN0bqiFViN wO{%K!h@TT}sP=YUc5m2iz7CGvueOmGlW)6yF(`H*oaxt4kQGo+e+cvMe@eooKL7v# diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/install-app.gradle b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/install-app.gradle deleted file mode 100644 index 89744abfe34..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/install-app.gradle +++ /dev/null @@ -1,33 +0,0 @@ -buildscript { - repositories { - mavenLocal() - } - dependencies { - classpath("org.springframework.boot:spring-boot-gradle-plugin:${project.bootVersion}") - } -} - -apply plugin: 'java' -apply plugin: 'maven' -apply plugin: 'org.springframework.boot' -apply plugin: 'application' - -group = 'installer' -version = '0.0.0' - -bootRun { - main = 'org.springframework.boot.SpringApplication' -} - -jar { - baseName = 'install-app' -} - -repositories { - mavenLocal() - mavenCentral() -} - -dependencies { - compile "org.springframework.boot:spring-boot-starter" -} diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/installer.gradle b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/installer.gradle deleted file mode 100644 index db0f6c0fbd8..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/installer.gradle +++ /dev/null @@ -1,40 +0,0 @@ -buildscript { - repositories { - mavenLocal() - } - dependencies { - classpath("org.springframework.boot:spring-boot-gradle-plugin:${project.bootVersion}") - } -} - -apply plugin: 'java' -apply plugin: 'maven' -apply plugin: 'org.springframework.boot' - -group = 'installer' -version = '0.0.0' - -install { - repositories.mavenInstaller { - pom.project { - parent { - groupId 'org.springframework.boot' - artifactId 'spring-boot-starter-parent' - version "${project.bootVersion}" - } - } - } -} - -jar { - baseName = 'installer' -} - -repositories { - mavenLocal() - mavenCentral() -} - -dependencies { - compile "org.springframework.boot:spring-boot-starter" -} diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/main-class.gradle b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/main-class.gradle deleted file mode 100644 index 7688b62d8a3..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/main-class.gradle +++ /dev/null @@ -1,39 +0,0 @@ -buildscript { - repositories { - mavenLocal() - } - dependencies { - classpath("org.springframework.boot:spring-boot-gradle-plugin:${project.bootVersion}") - } -} - -apply plugin: 'java' -apply plugin: 'maven' -apply plugin: 'org.springframework.boot' - -group = 'installer' -version = '0.0.0' - -if (project.hasProperty('bootRunMain')) { - bootRun { - main = 'org.springframework.boot.SpringApplication' - } -} - -if (project.hasProperty('nonJavaExecRun')) { - task run { } -} - - -jar { - baseName = 'installer' -} - -repositories { - mavenLocal() - mavenCentral() -} - -dependencies { - compile "org.springframework.boot:spring-boot-starter" -} diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/mixed-version-repackaging.gradle b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/mixed-version-repackaging.gradle deleted file mode 100644 index 9b2ef45dfa3..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/mixed-version-repackaging.gradle +++ /dev/null @@ -1,30 +0,0 @@ -buildscript { - repositories { - mavenLocal() - mavenCentral() - } - dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:${project.bootVersion}" - } -} - -repositories { - mavenLocal() - mavenCentral() -} - -apply plugin: 'org.springframework.boot' -apply plugin: 'java' - -dependencies { - compile 'com.google.guava:guava:16.0' - runtime 'com.google.guava:guava:18.0' -} - -dependencyManagement { - overriddenByDependencies = false -} - -springBoot { - mainClass = 'foo.bar.Baz' -} diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/multi-project-common-file-dependency/build.gradle b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/multi-project-common-file-dependency/build.gradle deleted file mode 100644 index 9285bfc05b4..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/multi-project-common-file-dependency/build.gradle +++ /dev/null @@ -1,32 +0,0 @@ -buildscript { - repositories { - mavenLocal() - } - dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:${project.bootVersion}" - } -} - -subprojects { - apply plugin: 'java' - - dependencies { - compile rootProject.files {'lib/foo.jar'} - } -} - -apply plugin: 'org.springframework.boot' - -repositories { - mavenLocal() -} - -springBoot { - mainClass = 'foo.bar.Baz' -} - -dependencies { - compile project(':one') - compile project(':two') -} - diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/multi-project-common-file-dependency/lib/foo.jar b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/multi-project-common-file-dependency/lib/foo.jar deleted file mode 100644 index 6b017f90e884d32125c6b332115fc67370c09e18..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2533 zcmah~2{@E%8y*H@OfeWCMTMpTht&XlC$#(gznPcA~ z>l`Y^TBwFBNgPx^C0ldyf77v@QvZ8h@B4n=^*;A~J@0cp?|oxXU^Xrgb4X$lx?evw z7AUZz^~|&t4NZ{Bn2j(_&>r~DJnhL zi&B*9>FTY-stl-(zwuEp>SrDu`nNncraDU=7;NEBZV_ci6?rU6KU^ zvSI^)_5gV~`dxB&cE-CXxs!a0O*>6mRp1TNc5m~bd@&-?iv#=#JTFcRpG*zx9+G=E z_FCF3&peAQsQi@XQdhg1e8u(nq2!LaPU{gFTGd~Ttr#hcw?1Y?6#ADEyZGZKZUs$y zS(e5_{Hd)9EG}y%1pT6*B9Wx%gSp1+o^Sv2#wXS#Bqds?IyO~3C^_TKAf_p|F=1wS zkf?ft%Vs3nIH8v3?ibs8hl?ln#_k`_^2eo%%}98p#GnusoCH{x6M>V;H_YJzc z&Rv^GbJ6thpH#lEuPR)MG_T#KEe!bp#~5+-g^GNLl_MAGE4^QK%xWFdi4?h}Q8}ti z=J|Iu18a|u)&NzU1tzl@ke;4OPVNpQk|MgBn`#eFAG9+c3IwbFvd9t>-&XiaD`*{O zx01UiH5jBU6B`Mc$2eZ}Po!tiJ9<8U3}#6Y_w5rGaw`^1xi+uhAwkpgzELw{9$ZPZ3fy1Ad=zhx;V5UI2OuxlS0Tdsb`DaW!pT z?)y&X0WP=I=|l&AyvJ6*e;@p>e*Y)f7fGMLmpxI3jGJ-v~ct)?zZ7c4UZ{u z8`XcvBxXG${x!p^^8uIiLb7HkA#{0UHGSSOEwA-xVq82USPe;aZTFFL%#@kVQ4ToB zSk8%eh3~@_2@QgK!NDax`UyBef)K&E+vhBo&Ou?mBUJV0q~HkF7X{QzqJZZ@S~mwV zpF;y0arn!zW}KEQGB{Hn?L#w8pCHg`OWCA`FyQ;sje)cC8rQs6<`O@TpVqo4kdRGn zfvNIVF1lreI3MFIfBlD6ECRnE`@5RyiME^Uu(Ow&B+Vjk8(>ugCNg0lFGc`4Ea^#H zJ3K(T$f`TX+mF(`5`3j{B``#A!tcM%m`3}uL#FUoQH2Hv>|p~uqUUrQ=1R-tY%+_2 z+5=qq9G_pZk77Gr>=q~O8s|Z`BedKHZLf zA=&drG?$tYiGE_%Q}I195uYhxqLmSE4yzFlna%}Lx=OAt^8O9yvZ`LYy_k-}g`G3a zMigl3eR}Y;akVaoQEIy?mzCY3M#h(1d88%mWPvE}T(F|`)wLtuU8!-Q%dnqm>z8fl z{BX3c$aNO>V)%Y-v&15<;_fkPBXar8TM!!Y^X)k6%Hq=oLv`HsMfBu+JU&(Ft>)$W z=P?qUpefyDv;LO6De`dS`!X>4rBhJNnq@bJ6m5qnWmywhny-lXt@4$<{!-TzE$s=0 zC#j0f=JLa02fvwV{8;$f`7&`$%5oxQjO$j@xU3pUbRIGz(!=i?_sBy%P<*kUER(0! zYiUXkN~IpZ3M1Nt4hx{2|MYEiY$k>*t4Hv@z^~D)P!=Dtk8}TCx|d7u2_uZCtn)xA z*b1pTZ&MhL3iPEo6K8RcjFIHY@^+nzee_$<0e&3*EL3IyqtA)(1L;Y_lWe(*NjNFaS$Yo?jn~$DJLGEqB6|<3d}Af z#_LuPo(0?w8O@=3Za!~L5Cw1o4y%eRrbD>`Q}+KqBQGCMFT9WMB|OR0|6Dgp(MZ7% zecpg#Hjh?7b-%`P%c9M_6wsC`M=&aCWk=L%SdXMf=!OOBI+aumWKVQ4PF~bK1wI8H zu|M-T%${c*Ub2|Yc*@#~7$y%)@RhV>le?R~z0sgV?0vQ7X z4tl4}+_$u88@9U-GlQ8UH(Ul-w_rP5c_+hej`;+c2Bt1=GZ_AWVN0bqiFViN wO{%K!h@TT}sP=YUc5m2iz7CGvueOmGlW)6yF(`H*oaxt4kQGo+e+cvMe@eooKL7v# diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/multi-project-common-file-dependency/settings.gradle b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/multi-project-common-file-dependency/settings.gradle deleted file mode 100644 index 9ae3a92d296..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/multi-project-common-file-dependency/settings.gradle +++ /dev/null @@ -1,3 +0,0 @@ - -include 'one' -include 'two' \ No newline at end of file diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/multi-project-runtime-project-dependency/build.gradle b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/multi-project-runtime-project-dependency/build.gradle deleted file mode 100644 index e33e5c4baff..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/multi-project-runtime-project-dependency/build.gradle +++ /dev/null @@ -1,25 +0,0 @@ -buildscript { - repositories { - mavenLocal() - } - dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:${project.bootVersion}" - } -} - -project(':projectA') { - apply plugin: 'org.springframework.boot' - repositories { - mavenLocal() - } - dependencies { - runtime project(':projectB') - } - bootRepackage { - mainClass 'com.foo.Bar' - } -} - -project(':projectB') { - apply plugin: 'java' -} \ No newline at end of file diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/multi-project-runtime-project-dependency/settings.gradle b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/multi-project-runtime-project-dependency/settings.gradle deleted file mode 100644 index ee6b6cd92c0..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/multi-project-runtime-project-dependency/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -include 'projectA', 'projectB' \ No newline at end of file diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/multi-project-transitive-file-dependency/build.gradle b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/multi-project-transitive-file-dependency/build.gradle deleted file mode 100644 index 7b1e7ab95bb..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/multi-project-transitive-file-dependency/build.gradle +++ /dev/null @@ -1,40 +0,0 @@ -buildscript { - repositories { - mavenLocal() - } - dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:${project.bootVersion}" - } -} - -project('main') { - apply plugin: 'org.springframework.boot' - apply plugin: 'java' - - repositories { - mavenLocal() - mavenCentral() - } - - dependencies { - compile project(':common') - } - - springBoot { - mainClass = 'foo.bar.Baz' - } -} - -project('common') { - apply plugin: 'java' - - repositories { - mavenLocal() - mavenCentral() - } - - dependencies { - compile "commons-logging:commons-logging:1.1.3" - compile files { "lib/foo.jar" } - } -} \ No newline at end of file diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/multi-project-transitive-file-dependency/common/lib/foo.jar b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/multi-project-transitive-file-dependency/common/lib/foo.jar deleted file mode 100644 index 6b017f90e884d32125c6b332115fc67370c09e18..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2533 zcmah~2{@E%8y*H@OfeWCMTMpTht&XlC$#(gznPcA~ z>l`Y^TBwFBNgPx^C0ldyf77v@QvZ8h@B4n=^*;A~J@0cp?|oxXU^Xrgb4X$lx?evw z7AUZz^~|&t4NZ{Bn2j(_&>r~DJnhL zi&B*9>FTY-stl-(zwuEp>SrDu`nNncraDU=7;NEBZV_ci6?rU6KU^ zvSI^)_5gV~`dxB&cE-CXxs!a0O*>6mRp1TNc5m~bd@&-?iv#=#JTFcRpG*zx9+G=E z_FCF3&peAQsQi@XQdhg1e8u(nq2!LaPU{gFTGd~Ttr#hcw?1Y?6#ADEyZGZKZUs$y zS(e5_{Hd)9EG}y%1pT6*B9Wx%gSp1+o^Sv2#wXS#Bqds?IyO~3C^_TKAf_p|F=1wS zkf?ft%Vs3nIH8v3?ibs8hl?ln#_k`_^2eo%%}98p#GnusoCH{x6M>V;H_YJzc z&Rv^GbJ6thpH#lEuPR)MG_T#KEe!bp#~5+-g^GNLl_MAGE4^QK%xWFdi4?h}Q8}ti z=J|Iu18a|u)&NzU1tzl@ke;4OPVNpQk|MgBn`#eFAG9+c3IwbFvd9t>-&XiaD`*{O zx01UiH5jBU6B`Mc$2eZ}Po!tiJ9<8U3}#6Y_w5rGaw`^1xi+uhAwkpgzELw{9$ZPZ3fy1Ad=zhx;V5UI2OuxlS0Tdsb`DaW!pT z?)y&X0WP=I=|l&AyvJ6*e;@p>e*Y)f7fGMLmpxI3jGJ-v~ct)?zZ7c4UZ{u z8`XcvBxXG${x!p^^8uIiLb7HkA#{0UHGSSOEwA-xVq82USPe;aZTFFL%#@kVQ4ToB zSk8%eh3~@_2@QgK!NDax`UyBef)K&E+vhBo&Ou?mBUJV0q~HkF7X{QzqJZZ@S~mwV zpF;y0arn!zW}KEQGB{Hn?L#w8pCHg`OWCA`FyQ;sje)cC8rQs6<`O@TpVqo4kdRGn zfvNIVF1lreI3MFIfBlD6ECRnE`@5RyiME^Uu(Ow&B+Vjk8(>ugCNg0lFGc`4Ea^#H zJ3K(T$f`TX+mF(`5`3j{B``#A!tcM%m`3}uL#FUoQH2Hv>|p~uqUUrQ=1R-tY%+_2 z+5=qq9G_pZk77Gr>=q~O8s|Z`BedKHZLf zA=&drG?$tYiGE_%Q}I195uYhxqLmSE4yzFlna%}Lx=OAt^8O9yvZ`LYy_k-}g`G3a zMigl3eR}Y;akVaoQEIy?mzCY3M#h(1d88%mWPvE}T(F|`)wLtuU8!-Q%dnqm>z8fl z{BX3c$aNO>V)%Y-v&15<;_fkPBXar8TM!!Y^X)k6%Hq=oLv`HsMfBu+JU&(Ft>)$W z=P?qUpefyDv;LO6De`dS`!X>4rBhJNnq@bJ6m5qnWmywhny-lXt@4$<{!-TzE$s=0 zC#j0f=JLa02fvwV{8;$f`7&`$%5oxQjO$j@xU3pUbRIGz(!=i?_sBy%P<*kUER(0! zYiUXkN~IpZ3M1Nt4hx{2|MYEiY$k>*t4Hv@z^~D)P!=Dtk8}TCx|d7u2_uZCtn)xA z*b1pTZ&MhL3iPEo6K8RcjFIHY@^+nzee_$<0e&3*EL3IyqtA)(1L;Y_lWe(*NjNFaS$Yo?jn~$DJLGEqB6|<3d}Af z#_LuPo(0?w8O@=3Za!~L5Cw1o4y%eRrbD>`Q}+KqBQGCMFT9WMB|OR0|6Dgp(MZ7% zecpg#Hjh?7b-%`P%c9M_6wsC`M=&aCWk=L%SdXMf=!OOBI+aumWKVQ4PF~bK1wI8H zu|M-T%${c*Ub2|Yc*@#~7$y%)@RhV>le?R~z0sgV?0vQ7X z4tl4}+_$u88@9U-GlQ8UH(Ul-w_rP5c_+hej`;+c2Bt1=GZ_AWVN0bqiFViN wO{%K!h@TT}sP=YUc5m2iz7CGvueOmGlW)6yF(`H*oaxt4kQGo+e+cvMe@eooKL7v# diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/multi-project-transitive-file-dependency/settings.gradle b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/multi-project-transitive-file-dependency/settings.gradle deleted file mode 100644 index 567c0652675..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/multi-project-transitive-file-dependency/settings.gradle +++ /dev/null @@ -1,3 +0,0 @@ - -include 'main' -include 'common' \ No newline at end of file diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/nojar.gradle b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/nojar.gradle deleted file mode 100644 index b2e18726485..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/nojar.gradle +++ /dev/null @@ -1,30 +0,0 @@ -buildscript { - repositories { - mavenLocal() - } - dependencies { - classpath("org.springframework.boot:spring-boot-gradle-plugin:${project.bootVersion}") - } -} - -apply plugin: 'org.springframework.boot' - -group = 'nojar' -version = '0.0.0' - -jar { - enabled = false -} - -bootRepackage { - enabled = false -} - -repositories { - mavenLocal() - mavenCentral() -} - -dependencies { - compile 'org.springframework.boot:spring-boot-starter' -} diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/repackage.gradle b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/repackage.gradle deleted file mode 100644 index 5851ba39b53..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/repackage.gradle +++ /dev/null @@ -1,66 +0,0 @@ -buildscript { - repositories { - mavenLocal() - } - dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:${project.bootVersion}" - } -} - -repositories { - mavenLocal() - mavenCentral() -} - -apply plugin: 'org.springframework.boot' -apply plugin: 'java' - -dependencies { - compile 'org.springframework.boot:spring-boot-starter-freemarker' - compile 'org.springframework.boot:spring-boot-starter-web' - compile 'org.springframework.boot:spring-boot-devtools' - compile files("foo.jar") -} - -springBoot { - if (!project.hasProperty("noMainClass")) { - mainClass = 'foo.bar.Baz' - } - if (project.hasProperty("excludeDevtoolsOnExtension")) { - excludeDevtools = Boolean.valueOf(project.excludeDevtoolsOnExtension) - } -} - -bootRepackage { - enabled = Boolean.valueOf(project.repackage) - if (project.hasProperty("excludeDevtoolsOnBootRepackage")) { - excludeDevtools = Boolean.valueOf(project.excludeDevtoolsOnBootRepackage) - } -} - -task customJar(type: Jar) { - archiveName = 'custom.jar' - from sourceSets.main.output -} - -task sourcesJar(type: Jar, dependsOn: classes) { - classifier = 'sources' - from sourceSets.main.allSource -} - -artifacts { - archives sourcesJar -} - -task customRepackagedJar(type: BootRepackage, dependsOn: customJar) { - withJarTask = customJar -} - -task customRepackagedJarWithStringReference(type: BootRepackage, dependsOn: customJar) { - withJarTask = 'customJar' -} - -task customRepackagedJarWithOwnMainClass(type: BootRepackage, dependsOn: customJar) { - withJarTask = customJar - mainClass = 'foo.bar.Baz' -} diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/spring-loaded-jvm-args/build.gradle b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/spring-loaded-jvm-args/build.gradle deleted file mode 100644 index 18551e99f09..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/spring-loaded-jvm-args/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -buildscript { - repositories { - mavenLocal() - mavenCentral() - } - dependencies { - classpath("org.springframework.boot:spring-boot-gradle-plugin:${project.bootVersion}") - classpath("org.springframework:springloaded:${project.springLoadedVersion}") - } -} - -apply plugin: 'application' -apply plugin: 'java' -apply plugin: 'org.springframework.boot' - -repositories { - mavenLocal() - mavenCentral() -} - -dependencies { - compile("org.springframework.boot:spring-boot-starter") -} - -applicationDefaultJvmArgs = [ - "-DSOME_ARG=someValue" -] - -jar { - baseName = 'spring-loaded-jvm-args' -} \ No newline at end of file diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/spring-loaded-jvm-args/src/main/java/test/Application.java b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/spring-loaded-jvm-args/src/main/java/test/Application.java deleted file mode 100644 index a648170a304..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/spring-loaded-jvm-args/src/main/java/test/Application.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2012-2015 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 test; - -import java.io.File; -import java.io.FileWriter; -import java.io.PrintWriter; -import java.lang.management.ManagementFactory; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; -import org.springframework.util.Assert; - -public class Application { - - public static void main(String[] args) throws Exception { - PrintWriter writer = new PrintWriter(new FileWriter(new File("build/output.txt"))); - for (String argument: ManagementFactory.getRuntimeMXBean().getInputArguments()) { - writer.println(argument); - } - writer.close(); - } -} diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/spring-loaded-old-gradle/build.gradle b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/spring-loaded-old-gradle/build.gradle deleted file mode 100644 index 83ca8f70a27..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/spring-loaded-old-gradle/build.gradle +++ /dev/null @@ -1,26 +0,0 @@ -buildscript { - repositories { - mavenLocal() - mavenCentral() - } - dependencies { - classpath("org.springframework.boot:spring-boot-gradle-plugin:${project.bootVersion}") - classpath("org.springframework:springloaded:${project.springLoadedVersion}") - } -} - -apply plugin: 'java' -apply plugin: 'org.springframework.boot' - -repositories { - mavenLocal() - mavenCentral() -} - -dependencies { - compile("org.springframework.boot:spring-boot-starter") -} - -jar { - baseName = 'spring-loaded-old-gradle' -} \ No newline at end of file diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/spring-loaded-old-gradle/src/main/java/test/Application.java b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/spring-loaded-old-gradle/src/main/java/test/Application.java deleted file mode 100644 index a648170a304..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/spring-loaded-old-gradle/src/main/java/test/Application.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2012-2015 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 test; - -import java.io.File; -import java.io.FileWriter; -import java.io.PrintWriter; -import java.lang.management.ManagementFactory; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; -import org.springframework.util.Assert; - -public class Application { - - public static void main(String[] args) throws Exception { - PrintWriter writer = new PrintWriter(new FileWriter(new File("build/output.txt"))); - for (String argument: ManagementFactory.getRuntimeMXBean().getInputArguments()) { - writer.println(argument); - } - writer.close(); - } -} diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/starter-dependencies.gradle b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/starter-dependencies.gradle deleted file mode 100644 index f00c9d2de82..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/starter-dependencies.gradle +++ /dev/null @@ -1,60 +0,0 @@ -import org.gradle.api.artifacts.result.UnresolvedDependencyResult; - -buildscript { - repositories { - mavenLocal() - } - dependencies { - classpath("org.springframework.boot:spring-boot-gradle-plugin:${project.bootVersion}") - } -} - -repositories { - mavenLocal() - mavenCentral() -} - -configurations { - springBootStarter -} - -dependencies { - springBootStarter "org.springframework.boot:${project.starter}:${project.bootVersion}" -} - -apply plugin: 'org.springframework.boot' - -task checkCommonsLogging { - doFirst { - def commonsLogging = resolvedDependencies - .find { it.selected.id.group == 'commons-logging' } - if (commonsLogging) { - throw new GradleException("${project.starter} pulls in commons-logging") - } - } -} - -task checkSpring { - doFirst { - def wrongSpring = resolvedDependencies - .findAll{it.selected.id.group == 'org.springframework'} - .findAll{it.selected.id.version != project.springVersion} - .collect {it.selected.id} - if (wrongSpring) { - throw new GradleException("${project.starter} pulled in ${wrongSpring as Set}") - } - } -} - -def getResolvedDependencies() { - def allDependencies = configurations.springBootStarter.incoming - .resolutionResult.allDependencies - .split { it instanceof UnresolvedDependencyResult } - - def unresolved = allDependencies.first() - def resolved = allDependencies.last() - if (unresolved) { - throw new GradleException("Resolution of ${project.starter} failed: ${unresolved}") - } - resolved -} diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/war-packaging.gradle b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/war-packaging.gradle deleted file mode 100644 index af52898c6e1..00000000000 --- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/resources/war-packaging.gradle +++ /dev/null @@ -1,27 +0,0 @@ -import org.gradle.api.artifacts.result.UnresolvedDependencyResult; - -buildscript { - repositories { - mavenLocal() - } - dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:${project.bootVersion}" - } -} - -repositories { - mavenLocal() - mavenCentral() -} - -apply plugin: 'org.springframework.boot' -apply plugin: 'war' - -dependencies { - compile 'org.springframework.boot:spring-boot-starter-freemarker' - providedRuntime "org.springframework.boot:spring-boot-starter-$servletContainer" -} - -springBoot { - mainClass = 'foo.bar.Baz' -} \ No newline at end of file From 8d55801c4d2106970c53a159d3a5cc7431b8a06a Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 29 Mar 2017 13:30:33 +0100 Subject: [PATCH 20/30] Restructure the code to enforce separation of plugin logic and tasks --- .../spring-boot-gradle-plugin/build.gradle | 1 + .../bundling/BundlingPluginFeatures.java | 174 ------------------ .../gradle/{ => dsl}/SpringBootExtension.java | 49 +++-- .../boot/gradle/dsl/package-info.java | 20 ++ .../ApplicationPluginAction.java} | 33 ++-- .../DependencyManagementPluginAction.java} | 35 ++-- .../boot/gradle/plugin/JavaPluginAction.java | 111 +++++++++++ .../MainClassConvention.java} | 45 ++--- .../boot/gradle/plugin/MavenPluginAction.java | 58 ++++++ .../PluginApplicationAction.java} | 19 +- .../SinglePublishedArtifact.java | 6 +- .../boot/gradle/plugin/SpringBootPlugin.java | 76 ++++---- .../plugin/SpringBootSoftwareComponent.java | 82 +++++++++ .../boot/gradle/plugin/WarPluginAction.java | 64 +++++++ .../boot/gradle/plugin/package-info.java | 20 ++ .../boot/gradle/run/RunPluginFeatures.java | 70 ------- .../boot/gradle/run/SourceSets.java | 54 ------ .../application/CreateBootStartScripts.java | 7 +- .../tasks/application/package-info.java | 20 ++ .../{ => tasks}/buildinfo/BuildInfo.java | 2 +- .../gradle/tasks/buildinfo/package-info.java | 20 ++ .../{ => tasks}/bundling/BootArchive.java | 2 +- .../bundling/BootArchiveSupport.java | 9 +- .../gradle/{ => tasks}/bundling/BootJar.java | 12 +- .../gradle/{ => tasks}/bundling/BootWar.java | 12 +- .../bundling/BootZipCopyAction.java | 33 ++-- .../bundling/LaunchScriptConfiguration.java | 2 +- .../{ => tasks}/bundling/ZipCompression.java | 2 +- .../gradle/tasks/bundling/package-info.java | 20 ++ .../boot/gradle/{ => tasks}/run/BootRun.java | 4 +- .../boot/gradle/tasks/run/package-info.java | 20 ++ .../BuildInfoDslIntegrationTests.java | 5 +- ...nagementPluginActionIntegrationTests.java} | 7 +- .../buildinfo/BuildInfoIntegrationTests.java | 4 +- .../AbstractBootArchiveIntegrationTests.java | 2 +- .../bundling/AbstractBootArchiveTests.java | 2 +- .../bundling/BootJarIntegrationTests.java | 2 +- .../{ => tasks}/bundling/BootJarTests.java | 2 +- .../bundling/BootWarIntegrationTests.java | 2 +- .../{ => tasks}/bundling/BootWarTests.java | 2 +- .../bundling/MavenIntegrationTests.java | 2 +- .../MavenPublishingIntegrationTests.java | 2 +- .../{ => tasks}/bundling/PomCondition.java | 2 +- .../run/BootRunIntegrationTests.java | 4 +- ...tegrationTests-additionalProperties.gradle | 0 ...ildInfoDslIntegrationTests-basicJar.gradle | 0 ...ildInfoDslIntegrationTests-basicWar.gradle | 0 ...lIntegrationTests-classesDependency.gradle | 0 ...lIntegrationTests-jarWithCustomName.gradle | 0 ...lIntegrationTests-warWithCustomName.gradle | 0 ...gementPluginActionIntegrationTests.gradle} | 0 .../BuildInfoIntegrationTests.gradle | 2 +- ...pplicationPluginMainClassNameIsUsed.gradle | 0 .../bundling/BootJarIntegrationTests.gradle | 0 ...pplicationPluginMainClassNameIsUsed.gradle | 0 .../bundling/BootWarIntegrationTests.gradle | 0 ...tegrationTests-bootJarCanBeUploaded.gradle | 0 ...tegrationTests-bootWarCanBeUploaded.gradle | 0 ...egrationTests-bootJarCanBePublished.gradle | 0 ...egrationTests-bootWarCanBePublished.gradle | 0 ...pplicationPluginJvmArgumentsAreUsed.gradle | 0 ...pplicationPluginMainClassNameIsUsed.gradle | 0 ...tRunIntegrationTests-basicExecution.gradle | 0 ...ationTests-sourceResourcesCanBeUsed.gradle | 0 64 files changed, 633 insertions(+), 489 deletions(-) delete mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BundlingPluginFeatures.java rename spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/{ => dsl}/SpringBootExtension.java (70%) create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/dsl/package-info.java rename spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/{application/ApplicationPluginFeatures.java => plugin/ApplicationPluginAction.java} (81%) rename spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/{dependencymanagement/DependencyManagementPluginFeatures.java => plugin/DependencyManagementPluginAction.java} (54%) create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/JavaPluginAction.java rename spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/{MainClassResolver.java => plugin/MainClassConvention.java} (57%) create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/MavenPluginAction.java rename spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/{PluginFeatures.java => plugin/PluginApplicationAction.java} (63%) rename spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/{bundling => plugin}/SinglePublishedArtifact.java (90%) create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootSoftwareComponent.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/WarPluginAction.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/package-info.java delete mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/RunPluginFeatures.java delete mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/SourceSets.java rename spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/{ => tasks}/application/CreateBootStartScripts.java (86%) create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/application/package-info.java rename spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/{ => tasks}/buildinfo/BuildInfo.java (98%) create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/package-info.java rename spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/{ => tasks}/bundling/BootArchive.java (98%) rename spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/{ => tasks}/bundling/BootArchiveSupport.java (95%) rename spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/{ => tasks}/bundling/BootJar.java (93%) rename spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/{ => tasks}/bundling/BootWar.java (93%) rename spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/{ => tasks}/bundling/BootZipCopyAction.java (93%) rename spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/{ => tasks}/bundling/LaunchScriptConfiguration.java (97%) rename spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/{ => tasks}/bundling/ZipCompression.java (94%) create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/package-info.java rename spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/{ => tasks}/run/BootRun.java (92%) create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/run/package-info.java rename spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/{buildinfo => dsl}/BuildInfoDslIntegrationTests.java (96%) rename spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/{dependencymanagement/DependencyManagementIntegrationTests.java => plugin/DependencyManagementPluginActionIntegrationTests.java} (87%) rename spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/{ => tasks}/buildinfo/BuildInfoIntegrationTests.java (95%) rename spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/{ => tasks}/bundling/AbstractBootArchiveIntegrationTests.java (98%) rename spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/{ => tasks}/bundling/AbstractBootArchiveTests.java (99%) rename spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/{ => tasks}/bundling/BootJarIntegrationTests.java (93%) rename spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/{ => tasks}/bundling/BootJarTests.java (94%) rename spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/{ => tasks}/bundling/BootWarIntegrationTests.java (93%) rename spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/{ => tasks}/bundling/BootWarTests.java (97%) rename spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/{ => tasks}/bundling/MavenIntegrationTests.java (97%) rename spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/{ => tasks}/bundling/MavenPublishingIntegrationTests.java (97%) rename spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/{ => tasks}/bundling/PomCondition.java (98%) rename spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/{ => tasks}/run/BootRunIntegrationTests.java (97%) rename spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/{buildinfo => dsl}/BuildInfoDslIntegrationTests-additionalProperties.gradle (100%) rename spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/{buildinfo => dsl}/BuildInfoDslIntegrationTests-basicJar.gradle (100%) rename spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/{buildinfo => dsl}/BuildInfoDslIntegrationTests-basicWar.gradle (100%) rename spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/{buildinfo => dsl}/BuildInfoDslIntegrationTests-classesDependency.gradle (100%) rename spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/{buildinfo => dsl}/BuildInfoDslIntegrationTests-jarWithCustomName.gradle (100%) rename spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/{buildinfo => dsl}/BuildInfoDslIntegrationTests-warWithCustomName.gradle (100%) rename spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/{dependencymanagement/DependencyManagementIntegrationTests.gradle => plugin/DependencyManagementPluginActionIntegrationTests.gradle} (100%) rename spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/{ => tasks}/buildinfo/BuildInfoIntegrationTests.gradle (87%) rename spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/{ => tasks}/bundling/BootJarIntegrationTests-applicationPluginMainClassNameIsUsed.gradle (100%) rename spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/{ => tasks}/bundling/BootJarIntegrationTests.gradle (100%) rename spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/{ => tasks}/bundling/BootWarIntegrationTests-applicationPluginMainClassNameIsUsed.gradle (100%) rename spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/{ => tasks}/bundling/BootWarIntegrationTests.gradle (100%) rename spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/{ => tasks}/bundling/MavenIntegrationTests-bootJarCanBeUploaded.gradle (100%) rename spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/{ => tasks}/bundling/MavenIntegrationTests-bootWarCanBeUploaded.gradle (100%) rename spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/{ => tasks}/bundling/MavenPublishingIntegrationTests-bootJarCanBePublished.gradle (100%) rename spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/{ => tasks}/bundling/MavenPublishingIntegrationTests-bootWarCanBePublished.gradle (100%) rename spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/{ => tasks}/run/BootRunIntegrationTests-applicationPluginJvmArgumentsAreUsed.gradle (100%) rename spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/{ => tasks}/run/BootRunIntegrationTests-applicationPluginMainClassNameIsUsed.gradle (100%) rename spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/{ => tasks}/run/BootRunIntegrationTests-basicExecution.gradle (100%) rename spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/{ => tasks}/run/BootRunIntegrationTests-sourceResourcesCanBeUsed.gradle (100%) diff --git a/spring-boot-tools/spring-boot-gradle-plugin/build.gradle b/spring-boot-tools/spring-boot-gradle-plugin/build.gradle index 5bef3dcec65..5f3c795b5ac 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/build.gradle +++ b/spring-boot-tools/spring-boot-gradle-plugin/build.gradle @@ -1,6 +1,7 @@ plugins { id 'java' id 'eclipse' + id 'org.sonarqube' version '2.2.1' } repositories { diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BundlingPluginFeatures.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BundlingPluginFeatures.java deleted file mode 100644 index b1a262e61f6..00000000000 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BundlingPluginFeatures.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright 2012-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.boot.gradle.bundling; - -import java.util.Collections; -import java.util.Set; -import java.util.concurrent.Callable; -import java.util.function.Supplier; - -import org.gradle.api.Project; -import org.gradle.api.artifacts.Configuration; -import org.gradle.api.artifacts.ModuleDependency; -import org.gradle.api.artifacts.PublishArtifact; -import org.gradle.api.artifacts.maven.MavenResolver; -import org.gradle.api.attributes.Usage; -import org.gradle.api.file.FileCollection; -import org.gradle.api.internal.artifacts.publish.ArchivePublishArtifact; -import org.gradle.api.internal.attributes.Usages; -import org.gradle.api.internal.component.SoftwareComponentInternal; -import org.gradle.api.internal.component.UsageContext; -import org.gradle.api.plugins.JavaPlugin; -import org.gradle.api.plugins.JavaPluginConvention; -import org.gradle.api.plugins.WarPlugin; -import org.gradle.api.tasks.SourceSet; -import org.gradle.api.tasks.Upload; - -import org.springframework.boot.gradle.MainClassResolver; -import org.springframework.boot.gradle.PluginFeatures; - -/** - * {@link PluginFeatures} for the bundling of an application. - * - * @author Andy Wilkinson - */ -public class BundlingPluginFeatures implements PluginFeatures { - - private SinglePublishedArtifact singlePublishedArtifact; - - @Override - public void apply(Project project) { - this.singlePublishedArtifact = new SinglePublishedArtifact( - project.getConfigurations().create("bootArchives").getArtifacts()); - project.getPlugins().withType(JavaPlugin.class, - (javaPlugin) -> configureBootJarTask(project)); - project.getPlugins().withType(WarPlugin.class, - (warPlugin) -> configureBootWarTask(project)); - project.afterEvaluate(this::configureBootArchivesUpload); - } - - private void configureBootWarTask(Project project) { - BootWar bootWar = project.getTasks().create("bootWar", BootWar.class); - bootWar.providedClasspath(providedRuntimeConfiguration(project)); - ArchivePublishArtifact artifact = new ArchivePublishArtifact(bootWar); - this.singlePublishedArtifact.addCandidate(artifact); - project.getComponents().add(new BootSoftwareComponent(artifact, "bootWeb")); - bootWar.conventionMapping("mainClass", - mainClassConvention(project, bootWar::getClasspath)); - } - - private void configureBootJarTask(Project project) { - BootJar bootJar = project.getTasks().create("bootJar", BootJar.class); - bootJar.classpath((Callable) () -> { - JavaPluginConvention convention = project.getConvention() - .getPlugin(JavaPluginConvention.class); - SourceSet mainSourceSet = convention.getSourceSets() - .getByName(SourceSet.MAIN_SOURCE_SET_NAME); - return mainSourceSet.getRuntimeClasspath(); - }); - ArchivePublishArtifact artifact = new ArchivePublishArtifact(bootJar); - this.singlePublishedArtifact.addCandidate(artifact); - project.getComponents().add(new BootSoftwareComponent(artifact, "bootJava")); - bootJar.conventionMapping("mainClass", - mainClassConvention(project, bootJar::getClasspath)); - } - - private Callable mainClassConvention(Project project, - Supplier classpathSupplier) { - return () -> { - if (project.hasProperty("mainClassName")) { - return project.property("mainClassName"); - } - return new MainClassResolver(classpathSupplier.get()).resolveMainClass(); - }; - } - - private void configureBootArchivesUpload(Project project) { - Upload upload = project.getTasks().withType(Upload.class) - .findByName("uploadBootArchives"); - if (upload == null) { - return; - } - clearConfigurationMappings(upload); - } - - private void clearConfigurationMappings(Upload upload) { - upload.getRepositories().withType(MavenResolver.class, (resolver) -> { - resolver.getPom().getScopeMappings().getMappings().clear(); - }); - } - - private Configuration providedRuntimeConfiguration(Project project) { - return project.getConfigurations() - .getByName(WarPlugin.PROVIDED_RUNTIME_CONFIGURATION_NAME); - } - - /** - * {@link SofwareComponent} for a Spring Boot fat jar or war. - */ - private static final class BootSoftwareComponent - implements SoftwareComponentInternal { - - private final PublishArtifact artifact; - - private final String name; - - private BootSoftwareComponent(PublishArtifact artifact, String name) { - this.artifact = artifact; - this.name = name; - } - - @Override - public String getName() { - return this.name; - } - - @Override - public Set getUsages() { - return Collections.singleton(new BootUsageContext(this.artifact)); - } - - private static final class BootUsageContext implements UsageContext { - - private static final Usage USAGE = Usages.usage("master"); - - private final PublishArtifact artifact; - - private BootUsageContext(PublishArtifact artifact) { - this.artifact = artifact; - } - - @Override - public Usage getUsage() { - return USAGE; - } - - @Override - public Set getArtifacts() { - return Collections.singleton(this.artifact); - } - - @Override - public Set getDependencies() { - return Collections.emptySet(); - } - - } - - } - -} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/SpringBootExtension.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/dsl/SpringBootExtension.java similarity index 70% rename from spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/SpringBootExtension.java rename to spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/dsl/SpringBootExtension.java index ae0a73d3ac0..8cbb99bc6cc 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/SpringBootExtension.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/dsl/SpringBootExtension.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.gradle; +package org.springframework.boot.gradle.dsl; import java.io.File; import java.util.concurrent.Callable; @@ -26,10 +26,10 @@ import org.gradle.api.plugins.JavaPluginConvention; import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.bundling.Jar; -import org.springframework.boot.gradle.buildinfo.BuildInfo; +import org.springframework.boot.gradle.tasks.buildinfo.BuildInfo; /** - * Gradle DSL extension that provides the entry point to Spring Boot's DSL. + * Entry point to Spring Boot's Gradle DSL. * * @author Andy Wilkinson */ @@ -73,30 +73,39 @@ public class SpringBootExtension { public void buildInfo(Action configurer) { BuildInfo bootBuildInfo = this.project.getTasks().create("bootBuildInfo", BuildInfo.class); - this.project.getPlugins().withType(JavaPlugin.class, (plugin) -> { + this.project.getPlugins().withType(JavaPlugin.class, plugin -> { this.project.getTasks().getByName(JavaPlugin.CLASSES_TASK_NAME) .dependsOn(bootBuildInfo); bootBuildInfo.getConventionMapping().map("projectArtifact", - (Callable) () -> { - Jar artifactTask = (Jar) this.project.getTasks().findByName("bootWar"); - if (artifactTask == null) { - artifactTask = (Jar) this.project.getTasks().findByName("bootJar"); - } - String result = artifactTask == null ? null : artifactTask.getBaseName(); - return result; - }); - bootBuildInfo.getConventionMapping().map("destinationDir", - (Callable) () -> { - return new File( - this.project.getConvention().getPlugin(JavaPluginConvention.class) - .getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME) - .getOutput().getResourcesDir(), - "META-INF"); - }); + (Callable) () -> determineArtifactBaseName()); + bootBuildInfo.getConventionMapping() + .map("destinationDir", + (Callable) () -> new File( + determineMainSourceSetResourcesOutputDir(), + "META-INF")); }); if (configurer != null) { configurer.execute(bootBuildInfo); } } + private File determineMainSourceSetResourcesOutputDir() { + return this.project.getConvention().getPlugin(JavaPluginConvention.class) + .getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME).getOutput() + .getResourcesDir(); + } + + private String determineArtifactBaseName() { + Jar artifactTask = findArtifactTask(); + return artifactTask == null ? null : artifactTask.getBaseName(); + } + + private Jar findArtifactTask() { + Jar artifactTask = (Jar) this.project.getTasks().findByName("bootWar"); + if (artifactTask != null) { + return artifactTask; + } + return (Jar) this.project.getTasks().findByName("bootJar"); + } + } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/dsl/package-info.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/dsl/package-info.java new file mode 100644 index 00000000000..8dd9565304f --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/dsl/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-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. + */ + +/** + * Spring Boot Gradle DSL. + */ +package org.springframework.boot.gradle.dsl; diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/application/ApplicationPluginFeatures.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/ApplicationPluginAction.java similarity index 81% rename from spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/application/ApplicationPluginFeatures.java rename to spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/ApplicationPluginAction.java index 2eadf41ab6c..cf66561c3b7 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/application/ApplicationPluginFeatures.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/ApplicationPluginAction.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.gradle.application; +package org.springframework.boot.gradle.plugin; import java.io.File; import java.io.IOException; @@ -23,6 +23,7 @@ import java.io.StringWriter; import java.util.concurrent.Callable; import org.gradle.api.GradleException; +import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.distribution.Distribution; import org.gradle.api.distribution.DistributionContainer; @@ -32,22 +33,17 @@ import org.gradle.api.plugins.ApplicationPlugin; import org.gradle.api.plugins.ApplicationPluginConvention; import org.gradle.jvm.application.scripts.TemplateBasedScriptGenerator; -import org.springframework.boot.gradle.PluginFeatures; +import org.springframework.boot.gradle.tasks.application.CreateBootStartScripts; /** - * Features that are configured when the application plugin is applied. + * Action that is executed in response to the {@link ApplicationPlugin} being applied. * * @author Andy Wilkinson */ -public class ApplicationPluginFeatures implements PluginFeatures { +final class ApplicationPluginAction implements PluginApplicationAction { @Override - public void apply(Project project) { - project.getPlugins().withType(ApplicationPlugin.class, - (plugin) -> configureDistribution(project)); - } - - public void configureDistribution(Project project) { + public void execute(Project project) { ApplicationPluginConvention applicationConvention = project.getConvention() .getPlugin(ApplicationPluginConvention.class); DistributionContainer distributions = project.getExtensions() @@ -61,12 +57,12 @@ public class ApplicationPluginFeatures implements PluginFeatures { ((TemplateBasedScriptGenerator) bootStartScripts.getWindowsStartScriptGenerator()) .setTemplate(project.getResources().getText() .fromString(loadResource("/windowsStartScript.txt"))); - project.getConfigurations().all((configuration) -> { + project.getConfigurations().all(configuration -> { if ("bootArchives".equals(configuration.getName())) { - distribution.getContents().with(project.copySpec().into("lib") - .from((Callable) () -> { - return configuration.getArtifacts().getFiles(); - })); + distribution.getContents() + .with(project.copySpec().into("lib") + .from((Callable) () -> configuration + .getArtifacts().getFiles())); bootStartScripts.setClasspath(configuration.getArtifacts().getFiles()); } }); @@ -75,10 +71,15 @@ public class ApplicationPluginFeatures implements PluginFeatures { bootStartScripts.getConventionMapping().map("applicationName", () -> applicationConvention.getApplicationName()); CopySpec binCopySpec = project.copySpec().into("bin").from(bootStartScripts); - binCopySpec.setFileMode(0755); + binCopySpec.setFileMode(0x755); distribution.getContents().with(binCopySpec); } + @Override + public Class> getPluginClass() { + return ApplicationPlugin.class; + } + private String loadResource(String name) { InputStreamReader reader = new InputStreamReader( getClass().getResourceAsStream(name)); diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/dependencymanagement/DependencyManagementPluginFeatures.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/DependencyManagementPluginAction.java similarity index 54% rename from spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/dependencymanagement/DependencyManagementPluginFeatures.java rename to spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/DependencyManagementPluginAction.java index 16f0c7c6f26..acd7e5ed7a8 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/dependencymanagement/DependencyManagementPluginFeatures.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/DependencyManagementPluginAction.java @@ -14,48 +14,37 @@ * limitations under the License. */ -package org.springframework.boot.gradle.dependencymanagement; +package org.springframework.boot.gradle.plugin; import io.spring.gradle.dependencymanagement.DependencyManagementPlugin; import io.spring.gradle.dependencymanagement.dsl.DependencyManagementExtension; -import io.spring.gradle.dependencymanagement.dsl.ImportsHandler; import org.gradle.api.Action; +import org.gradle.api.Plugin; import org.gradle.api.Project; -import org.springframework.boot.gradle.PluginFeatures; - /** - * {@link PluginFeatures} to configure dependency management. + * {@link Action} that is performed in response to the {@link DependencyManagementPlugin} + * being applied. * * @author Andy Wilkinson - * @author Phillip Webb - * @since 1.3.0 */ -public class DependencyManagementPluginFeatures implements PluginFeatures { +final class DependencyManagementPluginAction implements PluginApplicationAction { - private static final String SPRING_BOOT_VERSION = DependencyManagementPluginFeatures.class + private static final String SPRING_BOOT_VERSION = DependencyManagementPluginAction.class .getPackage().getImplementationVersion(); private static final String SPRING_BOOT_BOM = "org.springframework.boot:spring-boot-dependencies:" + SPRING_BOOT_VERSION; @Override - public void apply(Project project) { - project.getPlugins().withType(DependencyManagementPlugin.class, - (plugin) -> configureDependencyManagement(project)); + public void execute(Project project) { + project.getExtensions().findByType(DependencyManagementExtension.class) + .imports(importsHandler -> importsHandler.mavenBom(SPRING_BOOT_BOM)); } - private void configureDependencyManagement(Project project) { - DependencyManagementExtension dependencyManagement = project.getExtensions() - .findByType(DependencyManagementExtension.class); - dependencyManagement.imports(new Action() { - - @Override - public void execute(ImportsHandler importsHandler) { - importsHandler.mavenBom(SPRING_BOOT_BOM); - } - - }); + @Override + public Class> getPluginClass() { + return DependencyManagementPlugin.class; } } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/JavaPluginAction.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/JavaPluginAction.java new file mode 100644 index 00000000000..86515e51d77 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/JavaPluginAction.java @@ -0,0 +1,111 @@ +/* + * Copyright 2012-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.boot.gradle.plugin; + +import java.util.Collections; +import java.util.concurrent.Callable; + +import org.gradle.api.Action; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.file.FileCollection; +import org.gradle.api.internal.artifacts.publish.ArchivePublishArtifact; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.plugins.JavaPluginConvention; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.compile.JavaCompile; + +import org.springframework.boot.gradle.tasks.bundling.BootJar; +import org.springframework.boot.gradle.tasks.run.BootRun; + +/** + * {@link Action} that is executed in response to the {@link JavaPlugin} being applied. + * + * @author Andy Wilkinson + */ +final class JavaPluginAction implements PluginApplicationAction { + + private final SinglePublishedArtifact singlePublishedArtifact; + + JavaPluginAction(SinglePublishedArtifact singlePublishedArtifact) { + this.singlePublishedArtifact = singlePublishedArtifact; + } + + @Override + public Class> getPluginClass() { + return JavaPlugin.class; + } + + @Override + public void execute(Project project) { + BootJar bootJar = configureBootJarTask(project); + configureArtifactPublication(project, bootJar); + configureBootRunTask(project); + configureUtf8Encoding(project); + } + + private BootJar configureBootJarTask(Project project) { + BootJar bootJar = project.getTasks().create(SpringBootPlugin.BOOT_JAR_TASK_NAME, + BootJar.class); + bootJar.classpath((Callable) () -> { + JavaPluginConvention convention = project.getConvention() + .getPlugin(JavaPluginConvention.class); + SourceSet mainSourceSet = convention.getSourceSets() + .getByName(SourceSet.MAIN_SOURCE_SET_NAME); + return mainSourceSet.getRuntimeClasspath(); + }); + bootJar.conventionMapping("mainClass", + new MainClassConvention(project, bootJar::getClasspath)); + return bootJar; + } + + private void configureArtifactPublication(Project project, BootJar bootJar) { + ArchivePublishArtifact artifact = new ArchivePublishArtifact(bootJar); + this.singlePublishedArtifact.addCandidate(artifact); + project.getComponents().add(new SpringBootSoftwareComponent(artifact, + SpringBootPlugin.BOOT_JAVA_SOFTWARE_COMPONENT_NAME)); + } + + private void configureBootRunTask(Project project) { + JavaPluginConvention javaConvention = project.getConvention() + .getPlugin(JavaPluginConvention.class); + BootRun run = project.getTasks().create("bootRun", BootRun.class); + run.setDescription("Run the project with support for " + + "auto-detecting main class and reloading static resources"); + run.setGroup("application"); + run.classpath(javaConvention.getSourceSets() + .findByName(SourceSet.MAIN_SOURCE_SET_NAME).getRuntimeClasspath()); + run.getConventionMapping().map("jvmArgs", () -> { + if (project.hasProperty("applicationDefaultJvmArgs")) { + return project.property("applicationDefaultJvmArgs"); + } + return Collections.emptyList(); + }); + run.conventionMapping("main", + new MainClassConvention(project, run::getClasspath)); + } + + private void configureUtf8Encoding(Project project) { + project.getTasks().withType(JavaCompile.class, + compile -> compile.doFirst(task -> { + if (compile.getOptions().getEncoding() == null) { + compile.getOptions().setEncoding("UTF-8"); + } + })); + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/MainClassResolver.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/MainClassConvention.java similarity index 57% rename from spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/MainClassResolver.java rename to spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/MainClassConvention.java index aa85cbf10a2..766ff3c7e5f 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/MainClassResolver.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/MainClassConvention.java @@ -14,54 +14,55 @@ * limitations under the License. */ -package org.springframework.boot.gradle; +package org.springframework.boot.gradle.plugin; import java.io.File; import java.io.IOException; import java.util.Objects; +import java.util.concurrent.Callable; +import java.util.function.Supplier; +import org.gradle.api.Project; import org.gradle.api.file.FileCollection; import org.springframework.boot.loader.tools.MainClassFinder; /** - * Resolves the main class for an application. + * A {@link Callable} that provide a convention for the project's main class name. * * @author Andy Wilkinson */ -public class MainClassResolver { +final class MainClassConvention implements Callable { private static final String SPRING_BOOT_APPLICATION_CLASS_NAME = "org.springframework.boot.autoconfigure.SpringBootApplication"; - private final FileCollection classpath; + private final Project project; - /** - * Creates a new {@code MainClassResolver} that will search - * directories in the given {@code classpath} for - * the application's main class. - * - * @param classpath the classpath - */ - public MainClassResolver(FileCollection classpath) { - this.classpath = classpath; + private final Supplier classpathSupplier; + + MainClassConvention(Project project, Supplier classpathSupplier) { + this.project = project; + this.classpathSupplier = classpathSupplier; + } + + @Override + public Object call() throws Exception { + if (this.project.hasProperty("mainClassName")) { + return this.project.property("mainClassName"); + } + return resolveMainClass(); } - /** - * Resolves the main class. - * - * @return the main class or {@code null} - */ - public String resolveMainClass() { - return this.classpath.filter(File::isDirectory).getFiles().stream() + private String resolveMainClass() { + return this.classpathSupplier.get().filter(File::isDirectory).getFiles().stream() .map(this::findMainClass).filter(Objects::nonNull).findFirst() .orElse(null); } private String findMainClass(File file) { try { - String result = MainClassFinder.findSingleMainClass(file, + return MainClassFinder.findSingleMainClass(file, SPRING_BOOT_APPLICATION_CLASS_NAME); - return result; } catch (IOException ex) { return null; diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/MavenPluginAction.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/MavenPluginAction.java new file mode 100644 index 00000000000..644ce661ef9 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/MavenPluginAction.java @@ -0,0 +1,58 @@ +/* + * Copyright 2012-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.boot.gradle.plugin; + +import org.gradle.api.Action; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.artifacts.maven.MavenResolver; +import org.gradle.api.plugins.MavenPlugin; +import org.gradle.api.tasks.Upload; + +/** + * {@link Action} that is executed in response to the {@link MavenPlugin} being applied. + * + * @author Andy Wilkinson + */ +final class MavenPluginAction implements PluginApplicationAction { + + private final String uploadTaskName; + + MavenPluginAction(String uploadTaskName) { + this.uploadTaskName = uploadTaskName; + } + + @Override + public Class> getPluginClass() { + return MavenPlugin.class; + } + + @Override + public void execute(Project project) { + project.getTasks().withType(Upload.class, upload -> { + if (this.uploadTaskName.equals(upload.getName())) { + project.afterEvaluate(evaluated -> clearConfigurationMappings(upload)); + } + }); + } + + private void clearConfigurationMappings(Upload upload) { + upload.getRepositories().withType(MavenResolver.class, + resolver -> resolver.getPom().getScopeMappings().getMappings().clear()); + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/PluginFeatures.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/PluginApplicationAction.java similarity index 63% rename from spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/PluginFeatures.java rename to spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/PluginApplicationAction.java index b4445396f68..5cfb9dec7f6 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/PluginFeatures.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/PluginApplicationAction.java @@ -14,23 +14,20 @@ * limitations under the License. */ -package org.springframework.boot.gradle; +package org.springframework.boot.gradle.plugin; +import org.gradle.api.Action; +import org.gradle.api.Plugin; import org.gradle.api.Project; /** - * A specific set of {@code org.gradle.api.Plugin} features applied via the - * {@code SpringBootPlugin}. + * An {@link Action} to be executed on a {@link Project} in response to a particular type + * of {@link Plugin} being applied. * - * @author Phillip Webb + * @author Andy Wilkinson */ -@FunctionalInterface -public interface PluginFeatures { +interface PluginApplicationAction extends Action { - /** - * Apply the features to the specified project. - * @param project the project to apply features to - */ - void apply(Project project); + Class> getPluginClass(); } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/SinglePublishedArtifact.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SinglePublishedArtifact.java similarity index 90% rename from spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/SinglePublishedArtifact.java rename to spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SinglePublishedArtifact.java index eeafb064c3c..5b081358e9c 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/SinglePublishedArtifact.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SinglePublishedArtifact.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.gradle.bundling; +package org.springframework.boot.gradle.plugin; import org.gradle.api.artifacts.PublishArtifact; import org.gradle.api.artifacts.PublishArtifactSet; @@ -25,7 +25,7 @@ import org.gradle.api.artifacts.PublishArtifactSet; * * @author Andy Wilkinson */ -class SinglePublishedArtifact { +final class SinglePublishedArtifact { private final PublishArtifactSet artifacts; @@ -37,7 +37,7 @@ class SinglePublishedArtifact { void addCandidate(PublishArtifact candidate) { if (this.currentArtifact == null || "war".equals(candidate.getExtension())) { - this.artifacts.clear(); + this.artifacts.remove(this.currentArtifact); this.artifacts.add(candidate); this.currentArtifact = candidate; } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java index 8bf5ef615fa..d2663fa49d5 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java @@ -16,20 +16,20 @@ package org.springframework.boot.gradle.plugin; -import org.gradle.api.Action; +import java.util.Arrays; +import java.util.List; + import org.gradle.api.Plugin; import org.gradle.api.Project; -import org.gradle.api.Task; -import org.gradle.api.tasks.compile.JavaCompile; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.component.SoftwareComponent; -import org.springframework.boot.gradle.SpringBootExtension; -import org.springframework.boot.gradle.application.ApplicationPluginFeatures; -import org.springframework.boot.gradle.bundling.BundlingPluginFeatures; -import org.springframework.boot.gradle.dependencymanagement.DependencyManagementPluginFeatures; -import org.springframework.boot.gradle.run.RunPluginFeatures; +import org.springframework.boot.gradle.dsl.SpringBootExtension; +import org.springframework.boot.gradle.tasks.bundling.BootJar; +import org.springframework.boot.gradle.tasks.bundling.BootWar; /** - * Gradle 'Spring Boot' {@link Plugin}. + * Gradle plugin for Spring Boot. * * @author Phillip Webb * @author Dave Syer @@ -37,33 +37,47 @@ import org.springframework.boot.gradle.run.RunPluginFeatures; */ public class SpringBootPlugin implements Plugin { - @Override - public void apply(Project project) { - project.getExtensions().create("springBoot", SpringBootExtension.class, - project); - new ApplicationPluginFeatures().apply(project); - new BundlingPluginFeatures().apply(project); - new RunPluginFeatures().apply(project); - new DependencyManagementPluginFeatures().apply(project); - project.getTasks().withType(JavaCompile.class).all(new SetUtf8EncodingAction()); - } + /** + * The name of the {@link Configuration} that contains Spring Boot archives. + */ + public static final String BOOT_ARCHIVES_CONFIURATION_NAME = "bootArchives"; - private static class SetUtf8EncodingAction implements Action { + /** + * The name of the {@link SoftwareComponent} for a Spring Boot Java application. + */ + public static final String BOOT_JAVA_SOFTWARE_COMPONENT_NAME = "bootJava"; - @Override - public void execute(final JavaCompile compile) { - compile.doFirst(new Action() { + /** + * The name of the {@link SoftwareComponent} for a Spring Boot Web application. + */ + public static final String BOOT_WEB_SOFTWARE_COMPONENT_NAME = "bootWeb"; - @Override - public void execute(Task t) { - if (compile.getOptions().getEncoding() == null) { - compile.getOptions().setEncoding("UTF-8"); - } - } + /** + * The name of the default {@link BootJar} task. + */ + public static final String BOOT_JAR_TASK_NAME = "bootJar"; - }); - } + /** + * The name of the default {@link BootWar} task. + */ + public static final String BOOT_WAR_TASK_NAME = "bootWar"; + @Override + public void apply(Project project) { + project.getExtensions().create("springBoot", SpringBootExtension.class, project); + Configuration bootArchives = project.getConfigurations() + .create(BOOT_ARCHIVES_CONFIURATION_NAME); + SinglePublishedArtifact singlePublishedArtifact = new SinglePublishedArtifact( + bootArchives.getArtifacts()); + List actions = Arrays.asList( + new JavaPluginAction(singlePublishedArtifact), + new WarPluginAction(singlePublishedArtifact), + new MavenPluginAction(bootArchives.getUploadTaskName()), + new DependencyManagementPluginAction(), new ApplicationPluginAction()); + for (PluginApplicationAction action : actions) { + project.getPlugins().withType(action.getPluginClass(), + plugin -> action.execute(project)); + } } } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootSoftwareComponent.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootSoftwareComponent.java new file mode 100644 index 00000000000..83f6b5dfc16 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootSoftwareComponent.java @@ -0,0 +1,82 @@ +/* + * Copyright 2012-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.boot.gradle.plugin; + +import java.util.Collections; +import java.util.Set; + +import org.gradle.api.artifacts.ModuleDependency; +import org.gradle.api.artifacts.PublishArtifact; +import org.gradle.api.attributes.Usage; +import org.gradle.api.internal.attributes.Usages; +import org.gradle.api.internal.component.SoftwareComponentInternal; +import org.gradle.api.internal.component.UsageContext; + +/** + * {@link org.gradle.api.component.SoftwareComponent} for a Spring Boot fat jar or war. + * + * @author Andy Wilkinson + */ +final class SpringBootSoftwareComponent implements SoftwareComponentInternal { + + private final PublishArtifact artifact; + + private final String name; + + SpringBootSoftwareComponent(PublishArtifact artifact, String name) { + this.artifact = artifact; + this.name = name; + } + + @Override + public String getName() { + return this.name; + } + + @Override + public Set getUsages() { + return Collections.singleton(new BootUsageContext(this.artifact)); + } + + private static final class BootUsageContext implements UsageContext { + + private static final Usage USAGE = Usages.usage("master"); + + private final PublishArtifact artifact; + + private BootUsageContext(PublishArtifact artifact) { + this.artifact = artifact; + } + + @Override + public Usage getUsage() { + return USAGE; + } + + @Override + public Set getArtifacts() { + return Collections.singleton(this.artifact); + } + + @Override + public Set getDependencies() { + return Collections.emptySet(); + } + + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/WarPluginAction.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/WarPluginAction.java new file mode 100644 index 00000000000..f5dec40f069 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/WarPluginAction.java @@ -0,0 +1,64 @@ +/* + * Copyright 2012-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.boot.gradle.plugin; + +import org.gradle.api.Action; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.internal.artifacts.publish.ArchivePublishArtifact; +import org.gradle.api.plugins.WarPlugin; + +import org.springframework.boot.gradle.tasks.bundling.BootWar; + +/** + * {@link Action} that is executed in response to the {@link WarPlugin} being applied. + * + * @author Andy Wilkinson + */ +class WarPluginAction implements PluginApplicationAction { + + private final SinglePublishedArtifact singlePublishedArtifact; + + WarPluginAction(SinglePublishedArtifact singlePublishedArtifact) { + this.singlePublishedArtifact = singlePublishedArtifact; + } + + @Override + public Class> getPluginClass() { + return WarPlugin.class; + } + + @Override + public void execute(Project project) { + BootWar bootWar = project.getTasks().create(SpringBootPlugin.BOOT_WAR_TASK_NAME, + BootWar.class); + bootWar.providedClasspath(providedRuntimeConfiguration(project)); + ArchivePublishArtifact artifact = new ArchivePublishArtifact(bootWar); + this.singlePublishedArtifact.addCandidate(artifact); + project.getComponents().add(new SpringBootSoftwareComponent(artifact, + SpringBootPlugin.BOOT_WEB_SOFTWARE_COMPONENT_NAME)); + bootWar.conventionMapping("mainClass", + new MainClassConvention(project, bootWar::getClasspath)); + } + + private Configuration providedRuntimeConfiguration(Project project) { + return project.getConfigurations() + .getByName(WarPlugin.PROVIDED_RUNTIME_CONFIGURATION_NAME); + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/package-info.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/package-info.java new file mode 100644 index 00000000000..28ebf0d2893 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-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. + */ + +/** + * Central classes for the Spring Boot Gradle plugin. + */ +package org.springframework.boot.gradle.plugin; diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/RunPluginFeatures.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/RunPluginFeatures.java deleted file mode 100644 index c7838b22a45..00000000000 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/RunPluginFeatures.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2012-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.boot.gradle.run; - -import java.util.Collections; -import java.util.concurrent.Callable; - -import org.gradle.api.Project; -import org.gradle.api.plugins.JavaPlugin; -import org.gradle.api.plugins.JavaPluginConvention; -import org.gradle.api.tasks.SourceSet; - -import org.springframework.boot.gradle.MainClassResolver; -import org.springframework.boot.gradle.PluginFeatures; - -/** - * {@link PluginFeatures} to add run support. - * - * @author Phillip Webb - * @author Andy Wilkinson - */ -public class RunPluginFeatures implements PluginFeatures { - - private static final String RUN_APP_TASK_NAME = "bootRun"; - - @Override - public void apply(Project project) { - project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> { - addBootRunTask(project); - }); - } - - private void addBootRunTask(Project project) { - JavaPluginConvention javaConvention = project.getConvention() - .getPlugin(JavaPluginConvention.class); - BootRun run = project.getTasks().create(RUN_APP_TASK_NAME, BootRun.class); - run.setDescription("Run the project with support for " - + "auto-detecting main class and reloading static resources"); - run.setGroup("application"); - run.classpath(javaConvention.getSourceSets() - .findByName(SourceSet.MAIN_SOURCE_SET_NAME).getRuntimeClasspath()); - run.getConventionMapping().map("jvmArgs", ((Callable) () -> { - if (project.hasProperty("applicationDefaultJvmArgs")) { - return project.property("applicationDefaultJvmArgs"); - } - return Collections.emptyList(); - })); - run.conventionMapping("main", () -> { - if (project.hasProperty("mainClassName")) { - return project.property("mainClassName"); - } - return new MainClassResolver(run.getClasspath()).resolveMainClass(); - }); - } - -} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/SourceSets.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/SourceSets.java deleted file mode 100644 index c722cc4f279..00000000000 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/SourceSets.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2012-2015 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.boot.gradle.run; - -import java.util.Collections; - -import org.gradle.api.Project; -import org.gradle.api.plugins.JavaPluginConvention; -import org.gradle.api.tasks.SourceSet; - -/** - * Utilities for working with {@link SourceSet}s. - * - * @author Dave Syer - * @author Phillip Webb - */ -final class SourceSets { - - private SourceSets() { - } - - public static SourceSet findMainSourceSet(Project project) { - for (SourceSet sourceSet : getJavaSourceSets(project)) { - if (SourceSet.MAIN_SOURCE_SET_NAME.equals(sourceSet.getName())) { - return sourceSet; - } - } - return null; - } - - private static Iterable getJavaSourceSets(Project project) { - JavaPluginConvention plugin = project.getConvention() - .getPlugin(JavaPluginConvention.class); - if (plugin == null) { - return Collections.emptyList(); - } - return plugin.getSourceSets(); - } - -} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/application/CreateBootStartScripts.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/application/CreateBootStartScripts.java similarity index 86% rename from spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/application/CreateBootStartScripts.java rename to spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/application/CreateBootStartScripts.java index 9f11a5d51ef..334cdcf8bfb 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/application/CreateBootStartScripts.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/application/CreateBootStartScripts.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.gradle.application; +package org.springframework.boot.gradle.tasks.application; import org.gradle.api.tasks.Optional; import org.gradle.jvm.application.tasks.CreateStartScripts; @@ -33,9 +33,4 @@ public class CreateBootStartScripts extends CreateStartScripts { return super.getMainClassName(); } - @Override - public void setMainClassName(String mainClassName) { - super.setMainClassName(mainClassName); - } - } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/application/package-info.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/application/package-info.java new file mode 100644 index 00000000000..3fdaffb3030 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/application/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-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. + */ + +/** + * Classes related to Gradle's application features. + */ +package org.springframework.boot.gradle.tasks.application; diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/buildinfo/BuildInfo.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfo.java similarity index 98% rename from spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/buildinfo/BuildInfo.java rename to spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfo.java index c88797ce611..1f4239e7020 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/buildinfo/BuildInfo.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfo.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.gradle.buildinfo; +package org.springframework.boot.gradle.tasks.buildinfo; import java.io.File; import java.io.IOException; diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/package-info.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/package-info.java new file mode 100644 index 00000000000..e107051c6b8 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-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. + */ + +/** + * Support for producing build info for consumption by Spring Boot's actuator. + */ +package org.springframework.boot.gradle.tasks.buildinfo; diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchive.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootArchive.java similarity index 98% rename from spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchive.java rename to spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootArchive.java index 3ab193e4933..2305491ac07 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchive.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootArchive.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.gradle.bundling; +package org.springframework.boot.gradle.tasks.bundling; import org.gradle.api.Action; import org.gradle.api.Project; diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchiveSupport.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootArchiveSupport.java similarity index 95% rename from spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchiveSupport.java rename to spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootArchiveSupport.java index 3d8acf6bd6c..003808cd36f 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchiveSupport.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootArchiveSupport.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.gradle.bundling; +package org.springframework.boot.gradle.tasks.bundling; import java.util.Collections; import java.util.HashSet; @@ -138,11 +138,10 @@ class BootArchiveSupport { @Override public WorkResult execute(CopyActionProcessingStream stream) { - return this.delegate.execute((action) -> { + return this.delegate.execute(action -> { Map detailsByPath = new TreeMap<>(); - stream.process((details) -> { - detailsByPath.put(details.getRelativePath(), details); - }); + stream.process( + details -> detailsByPath.put(details.getRelativePath(), details)); detailsByPath.values().stream().forEach(action::processFile); }); } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootJar.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootJar.java similarity index 93% rename from spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootJar.java rename to spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootJar.java index fc331f6edd1..15682398f7d 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootJar.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootJar.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.gradle.bundling; +package org.springframework.boot.gradle.tasks.bundling; import java.io.File; import java.util.Collections; @@ -51,12 +51,10 @@ public class BootJar extends Jar implements BootArchive { } private Action classpathFiles(Spec filter) { - return (copySpec) -> { - copySpec.from((Callable>) () -> { - return this.classpath == null ? Collections.emptyList() - : this.classpath.filter(filter); - }); - }; + return copySpec -> copySpec + .from((Callable>) () -> this.classpath == null + ? Collections.emptyList() : this.classpath.filter(filter)); + } @Override diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootWar.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootWar.java similarity index 93% rename from spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootWar.java rename to spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootWar.java index 8c243ed8644..8bf8ff15ba9 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootWar.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootWar.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.gradle.bundling; +package org.springframework.boot.gradle.tasks.bundling; import java.io.File; import java.util.Collections; @@ -45,12 +45,10 @@ public class BootWar extends War implements BootArchive { private FileCollection providedClasspath; public BootWar() { - getWebInf().into("lib-provided", (copySpec) -> { - copySpec.from((Callable>) () -> { - return this.providedClasspath == null ? Collections.emptyList() - : this.providedClasspath; - }); - }); + getWebInf().into("lib-provided", + copySpec -> copySpec + .from((Callable>) () -> this.providedClasspath == null + ? Collections.emptyList() : this.providedClasspath)); } @Override diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootZipCopyAction.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootZipCopyAction.java similarity index 93% rename from spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootZipCopyAction.java rename to spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootZipCopyAction.java index 0eaa6c38902..6a8086f21b3 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootZipCopyAction.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootZipCopyAction.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.gradle.bundling; +package org.springframework.boot.gradle.tasks.bundling; import java.io.File; import java.io.FileOutputStream; @@ -101,9 +101,7 @@ class BootZipCopyAction implements CopyAction { // Continue } } - return () -> { - return true; - }; + return () -> true; } private void writeLoaderClassesIfNecessary(ZipOutputStream out) { @@ -116,18 +114,9 @@ class BootZipCopyAction implements CopyAction { ZipEntry entry; try (ZipInputStream in = new ZipInputStream(getClass() .getResourceAsStream("/META-INF/loader/spring-boot-loader.jar"))) { - byte[] buffer = new byte[4096]; while ((entry = in.getNextEntry()) != null) { - if (entry.getName().endsWith((".class"))) { - if (!this.preserveFileTimestamps) { - entry.setTime(GUtil.CONSTANT_TIME_FOR_ZIP_ENTRIES); - } - out.putNextEntry(entry); - int read; - while ((read = in.read(buffer)) > 0) { - out.write(buffer, 0, read); - } - out.closeEntry(); + if (entry.getName().endsWith(".class")) { + writeClass(entry, in, out); } } } @@ -136,6 +125,20 @@ class BootZipCopyAction implements CopyAction { } } + private void writeClass(ZipEntry entry, ZipInputStream in, ZipOutputStream out) + throws IOException { + byte[] buffer = new byte[4096]; + if (!this.preserveFileTimestamps) { + entry.setTime(GUtil.CONSTANT_TIME_FOR_ZIP_ENTRIES); + } + out.putNextEntry(entry); + int read; + while ((read = in.read(buffer)) > 0) { + out.write(buffer, 0, read); + } + out.closeEntry(); + } + private void writeLaunchScriptIfNecessary(FileOutputStream fileStream) { try { if (this.launchScript.isIncluded()) { diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/LaunchScriptConfiguration.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/LaunchScriptConfiguration.java similarity index 97% rename from spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/LaunchScriptConfiguration.java rename to spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/LaunchScriptConfiguration.java index f302cc0f91f..3e31f768425 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/LaunchScriptConfiguration.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/LaunchScriptConfiguration.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.gradle.bundling; +package org.springframework.boot.gradle.tasks.bundling; import java.io.File; import java.io.IOException; diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/ZipCompression.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/ZipCompression.java similarity index 94% rename from spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/ZipCompression.java rename to spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/ZipCompression.java index b34e0793d85..9aa624732b1 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/ZipCompression.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/ZipCompression.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.gradle.bundling; +package org.springframework.boot.gradle.tasks.bundling; import java.util.zip.ZipEntry; diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/package-info.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/package-info.java new file mode 100644 index 00000000000..59747886129 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-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. + */ + +/** + * Support for creating executable jars and wars. + */ +package org.springframework.boot.gradle.tasks.bundling; diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/BootRun.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/run/BootRun.java similarity index 92% rename from spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/BootRun.java rename to spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/run/BootRun.java index 527e8336c2b..2a0b41cc0e5 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/BootRun.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/run/BootRun.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.gradle.run; +package org.springframework.boot.gradle.tasks.run; import org.gradle.api.file.SourceDirectorySet; import org.gradle.api.tasks.JavaExec; @@ -39,7 +39,7 @@ public class BootRun extends JavaExec { public void sourceResources(SourceSet sourceSet) { setClasspath(getProject() .files(sourceSet.getResources().getSrcDirs(), getClasspath()) - .filter((file) -> !file.equals(sourceSet.getOutput().getResourcesDir()))); + .filter(file -> !file.equals(sourceSet.getOutput().getResourcesDir()))); } @Override diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/run/package-info.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/run/package-info.java new file mode 100644 index 00000000000..a88fec5e61a --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/run/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-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. + */ + +/** + * Support for running Spring Boot applications. + */ +package org.springframework.boot.gradle.tasks.run; diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/dsl/BuildInfoDslIntegrationTests.java similarity index 96% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests.java rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/dsl/BuildInfoDslIntegrationTests.java index 01fd1a622fa..fe27924da2c 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/dsl/BuildInfoDslIntegrationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.gradle.buildinfo; +package org.springframework.boot.gradle.dsl; import java.io.File; import java.io.FileReader; @@ -25,13 +25,14 @@ import org.gradle.testkit.runner.TaskOutcome; import org.junit.Rule; import org.junit.Test; +import org.springframework.boot.gradle.tasks.buildinfo.BuildInfo; import org.springframework.boot.gradle.testkit.GradleBuild; import static org.assertj.core.api.Assertions.assertThat; /** * Integration tests for {@link BuildInfo} created using the - * {@link org.springframework.boot.gradle.SpringBootExtension DSL}. + * {@link org.springframework.boot.gradle.dsl.SpringBootExtension DSL}. * * @author Andy Wilkinson */ diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/dependencymanagement/DependencyManagementIntegrationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/DependencyManagementPluginActionIntegrationTests.java similarity index 87% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/dependencymanagement/DependencyManagementIntegrationTests.java rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/DependencyManagementPluginActionIntegrationTests.java index f03a4dcf4b2..42b5e87f35a 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/dependencymanagement/DependencyManagementIntegrationTests.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/DependencyManagementPluginActionIntegrationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.gradle.dependencymanagement; +package org.springframework.boot.gradle.plugin; import org.gradle.testkit.runner.TaskOutcome; import org.junit.Rule; @@ -25,11 +25,12 @@ import org.springframework.boot.gradle.testkit.GradleBuild; import static org.assertj.core.api.Assertions.assertThat; /** - * Integration tests for the plugin's dependency management features. + * Integration tests for the configuration applied by + * {@link DependencyManagementPluginAction}. * * @author Andy Wilkinson */ -public class DependencyManagementIntegrationTests { +public class DependencyManagementPluginActionIntegrationTests { @Rule public GradleBuild gradleBuild = new GradleBuild(); diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/buildinfo/BuildInfoIntegrationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.java similarity index 95% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/buildinfo/BuildInfoIntegrationTests.java rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.java index ddeac1bb973..41603256d10 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/buildinfo/BuildInfoIntegrationTests.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.gradle.buildinfo; +package org.springframework.boot.gradle.tasks.buildinfo; import org.gradle.testkit.runner.TaskOutcome; import org.junit.Rule; @@ -25,7 +25,7 @@ import org.springframework.boot.gradle.testkit.GradleBuild; import static org.assertj.core.api.Assertions.assertThat; /** - * Integration tests for {@link BuildInfo}. + * Integration tests for the {@link BuildInfo} task. * * @author Andy Wilkinson */ diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveIntegrationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/AbstractBootArchiveIntegrationTests.java similarity index 98% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveIntegrationTests.java rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/AbstractBootArchiveIntegrationTests.java index 11ad775d9d2..72d835d9af1 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveIntegrationTests.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/AbstractBootArchiveIntegrationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.gradle.bundling; +package org.springframework.boot.gradle.tasks.bundling; import java.io.File; import java.io.IOException; diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/AbstractBootArchiveTests.java similarity index 99% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveTests.java rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/AbstractBootArchiveTests.java index 876b98d5116..befdd65f619 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveTests.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/AbstractBootArchiveTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.gradle.bundling; +package org.springframework.boot.gradle.tasks.bundling; import java.io.File; import java.io.IOException; diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootJarIntegrationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests.java similarity index 93% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootJarIntegrationTests.java rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests.java index 6a70d391a4f..78182fac7d7 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootJarIntegrationTests.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.gradle.bundling; +package org.springframework.boot.gradle.tasks.bundling; /** * Integration tests for {@link BootJar}. diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootJarTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootJarTests.java similarity index 94% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootJarTests.java rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootJarTests.java index f54198e94e6..1dda05c1c11 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootJarTests.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootJarTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.gradle.bundling; +package org.springframework.boot.gradle.tasks.bundling; /** * Tests for {@link BootJar}. diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootWarIntegrationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootWarIntegrationTests.java similarity index 93% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootWarIntegrationTests.java rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootWarIntegrationTests.java index c6fbd9c73f4..437be974676 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootWarIntegrationTests.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootWarIntegrationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.gradle.bundling; +package org.springframework.boot.gradle.tasks.bundling; /** * Integration tests for {@link BootJar}. diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootWarTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootWarTests.java similarity index 97% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootWarTests.java rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootWarTests.java index 8278678b166..316ad520748 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootWarTests.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootWarTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.gradle.bundling; +package org.springframework.boot.gradle.tasks.bundling; import java.io.IOException; import java.util.jar.JarFile; diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/MavenIntegrationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/MavenIntegrationTests.java similarity index 97% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/MavenIntegrationTests.java rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/MavenIntegrationTests.java index 1954cc81700..5b1ac3088fd 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/MavenIntegrationTests.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/MavenIntegrationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.gradle.bundling; +package org.springframework.boot.gradle.tasks.bundling; import java.io.File; import java.io.FileNotFoundException; diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/MavenPublishingIntegrationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/MavenPublishingIntegrationTests.java similarity index 97% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/MavenPublishingIntegrationTests.java rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/MavenPublishingIntegrationTests.java index 2f4ad97a9b5..a2dce669f9d 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/MavenPublishingIntegrationTests.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/MavenPublishingIntegrationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.gradle.bundling; +package org.springframework.boot.gradle.tasks.bundling; import java.io.File; import java.io.FileNotFoundException; diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/PomCondition.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/PomCondition.java similarity index 98% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/PomCondition.java rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/PomCondition.java index f05fff957ee..b3d8f293a66 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/PomCondition.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/PomCondition.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.gradle.bundling; +package org.springframework.boot.gradle.tasks.bundling; import java.io.File; import java.io.FileReader; diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/run/BootRunIntegrationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/run/BootRunIntegrationTests.java similarity index 97% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/run/BootRunIntegrationTests.java rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/run/BootRunIntegrationTests.java index 120a211be7c..a22a9db72a5 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/run/BootRunIntegrationTests.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/run/BootRunIntegrationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.gradle.run; +package org.springframework.boot.gradle.tasks.run; import java.io.File; import java.io.IOException; @@ -30,7 +30,7 @@ import org.springframework.util.FileSystemUtils; import static org.assertj.core.api.Assertions.assertThat; /** - * Integration tests for {@link BootRun}. + * Integration tests for the {@link BootRun} task. * * @author Andy Wilkinson */ diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-additionalProperties.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/dsl/BuildInfoDslIntegrationTests-additionalProperties.gradle similarity index 100% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-additionalProperties.gradle rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/dsl/BuildInfoDslIntegrationTests-additionalProperties.gradle diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-basicJar.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/dsl/BuildInfoDslIntegrationTests-basicJar.gradle similarity index 100% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-basicJar.gradle rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/dsl/BuildInfoDslIntegrationTests-basicJar.gradle diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-basicWar.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/dsl/BuildInfoDslIntegrationTests-basicWar.gradle similarity index 100% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-basicWar.gradle rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/dsl/BuildInfoDslIntegrationTests-basicWar.gradle diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-classesDependency.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/dsl/BuildInfoDslIntegrationTests-classesDependency.gradle similarity index 100% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-classesDependency.gradle rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/dsl/BuildInfoDslIntegrationTests-classesDependency.gradle diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-jarWithCustomName.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/dsl/BuildInfoDslIntegrationTests-jarWithCustomName.gradle similarity index 100% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-jarWithCustomName.gradle rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/dsl/BuildInfoDslIntegrationTests-jarWithCustomName.gradle diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-warWithCustomName.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/dsl/BuildInfoDslIntegrationTests-warWithCustomName.gradle similarity index 100% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoDslIntegrationTests-warWithCustomName.gradle rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/dsl/BuildInfoDslIntegrationTests-warWithCustomName.gradle diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/dependencymanagement/DependencyManagementIntegrationTests.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/DependencyManagementPluginActionIntegrationTests.gradle similarity index 100% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/dependencymanagement/DependencyManagementIntegrationTests.gradle rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/DependencyManagementPluginActionIntegrationTests.gradle diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoIntegrationTests.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.gradle similarity index 87% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoIntegrationTests.gradle rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.gradle index fe973b6bee5..23db1b2d8b5 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/buildinfo/BuildInfoIntegrationTests.gradle +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.gradle @@ -8,7 +8,7 @@ def property(String name, Object defaultValue) { project.hasProperty(name) ? project.getProperty(name) : defaultValue } -task buildInfo(type: org.springframework.boot.gradle.buildinfo.BuildInfo) { +task buildInfo(type: org.springframework.boot.gradle.tasks.buildinfo.BuildInfo) { destinationDir file(property('buildInfoDestinationDir', project.buildDir)) projectArtifact property('buildInfoProjectArtifact', 'foo') projectVersion property('buildInfoProjectVersion', '1.0') diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/BootJarIntegrationTests-applicationPluginMainClassNameIsUsed.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests-applicationPluginMainClassNameIsUsed.gradle similarity index 100% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/BootJarIntegrationTests-applicationPluginMainClassNameIsUsed.gradle rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests-applicationPluginMainClassNameIsUsed.gradle diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/BootJarIntegrationTests.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests.gradle similarity index 100% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/BootJarIntegrationTests.gradle rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests.gradle diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/BootWarIntegrationTests-applicationPluginMainClassNameIsUsed.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootWarIntegrationTests-applicationPluginMainClassNameIsUsed.gradle similarity index 100% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/BootWarIntegrationTests-applicationPluginMainClassNameIsUsed.gradle rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootWarIntegrationTests-applicationPluginMainClassNameIsUsed.gradle diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/BootWarIntegrationTests.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootWarIntegrationTests.gradle similarity index 100% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/BootWarIntegrationTests.gradle rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootWarIntegrationTests.gradle diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/MavenIntegrationTests-bootJarCanBeUploaded.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/MavenIntegrationTests-bootJarCanBeUploaded.gradle similarity index 100% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/MavenIntegrationTests-bootJarCanBeUploaded.gradle rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/MavenIntegrationTests-bootJarCanBeUploaded.gradle diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/MavenIntegrationTests-bootWarCanBeUploaded.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/MavenIntegrationTests-bootWarCanBeUploaded.gradle similarity index 100% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/MavenIntegrationTests-bootWarCanBeUploaded.gradle rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/MavenIntegrationTests-bootWarCanBeUploaded.gradle diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/MavenPublishingIntegrationTests-bootJarCanBePublished.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/MavenPublishingIntegrationTests-bootJarCanBePublished.gradle similarity index 100% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/MavenPublishingIntegrationTests-bootJarCanBePublished.gradle rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/MavenPublishingIntegrationTests-bootJarCanBePublished.gradle diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/MavenPublishingIntegrationTests-bootWarCanBePublished.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/MavenPublishingIntegrationTests-bootWarCanBePublished.gradle similarity index 100% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/MavenPublishingIntegrationTests-bootWarCanBePublished.gradle rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/MavenPublishingIntegrationTests-bootWarCanBePublished.gradle diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/run/BootRunIntegrationTests-applicationPluginJvmArgumentsAreUsed.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/run/BootRunIntegrationTests-applicationPluginJvmArgumentsAreUsed.gradle similarity index 100% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/run/BootRunIntegrationTests-applicationPluginJvmArgumentsAreUsed.gradle rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/run/BootRunIntegrationTests-applicationPluginJvmArgumentsAreUsed.gradle diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/run/BootRunIntegrationTests-applicationPluginMainClassNameIsUsed.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/run/BootRunIntegrationTests-applicationPluginMainClassNameIsUsed.gradle similarity index 100% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/run/BootRunIntegrationTests-applicationPluginMainClassNameIsUsed.gradle rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/run/BootRunIntegrationTests-applicationPluginMainClassNameIsUsed.gradle diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/run/BootRunIntegrationTests-basicExecution.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/run/BootRunIntegrationTests-basicExecution.gradle similarity index 100% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/run/BootRunIntegrationTests-basicExecution.gradle rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/run/BootRunIntegrationTests-basicExecution.gradle diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/run/BootRunIntegrationTests-sourceResourcesCanBeUsed.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/run/BootRunIntegrationTests-sourceResourcesCanBeUsed.gradle similarity index 100% rename from spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/run/BootRunIntegrationTests-sourceResourcesCanBeUsed.gradle rename to spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/run/BootRunIntegrationTests-sourceResourcesCanBeUsed.gradle From 201ea133e100632a9f609c15ef2efb993f7752e4 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 29 Mar 2017 17:41:17 +0100 Subject: [PATCH 21/30] Guide users to apply dependency management plugin when resolution fails --- .../boot/gradle/plugin/SpringBootPlugin.java | 8 +++ .../UnresolvedDependenciesAnalyzer.java | 69 +++++++++++++++++++ ...anagementPluginActionIntegrationTests.java | 24 +++++++ .../boot/gradle/testkit/GradleBuild.java | 33 ++++++--- ...VersionlessDependencyFailsToResolve.gradle | 16 +++++ 5 files changed, 140 insertions(+), 10 deletions(-) create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/UnresolvedDependenciesAnalyzer.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/DependencyManagementPluginActionIntegrationTests-helpfulErrorWhenVersionlessDependencyFailsToResolve.gradle diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java index d2663fa49d5..ba5ed878076 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java @@ -78,6 +78,14 @@ public class SpringBootPlugin implements Plugin { project.getPlugins().withType(action.getPluginClass(), plugin -> action.execute(project)); } + UnresolvedDependenciesAnalyzer unresolvedDependenciesAnalyzer = new UnresolvedDependenciesAnalyzer(); + project.getConfigurations().all(configuration -> configuration.getIncoming() + .afterResolve(resolvableDependencies -> unresolvedDependenciesAnalyzer + .analyze(configuration.getResolvedConfiguration() + .getLenientConfiguration() + .getUnresolvedModuleDependencies()))); + project.getGradle().buildFinished( + buildResult -> unresolvedDependenciesAnalyzer.buildFinished(project)); } } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/UnresolvedDependenciesAnalyzer.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/UnresolvedDependenciesAnalyzer.java new file mode 100644 index 00000000000..621d01d45b2 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/UnresolvedDependenciesAnalyzer.java @@ -0,0 +1,69 @@ +/* + * Copyright 2012-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.boot.gradle.plugin; + +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; + +import io.spring.gradle.dependencymanagement.DependencyManagementPlugin; +import org.gradle.api.Project; +import org.gradle.api.artifacts.ModuleVersionSelector; +import org.gradle.api.artifacts.UnresolvedDependency; +import org.slf4j.LoggerFactory; + +/** + * An analyzer for {@link UnresolvedDependency unresolvable dependencies} that logs a + * warning suggesting that the {@code io.spring.dependency-management} plugin is applied + * when one or more versionless dependencies fails to resolve. + * + * @author Andy Wilkinson + */ +class UnresolvedDependenciesAnalyzer { + + private static final org.slf4j.Logger logger = LoggerFactory + .getLogger(SpringBootPlugin.class); + + private Set dependenciesWithNoVersion = new HashSet<>(); + + public void analyze(Set unresolvedDependencies) { + this.dependenciesWithNoVersion = unresolvedDependencies.stream() + .map(unresolvedDependency -> unresolvedDependency.getSelector()) + .filter(this::hasNoVersion).collect(Collectors.toSet()); + } + + void buildFinished(Project project) { + if (!this.dependenciesWithNoVersion.isEmpty() + && !project.getPlugins().hasPlugin(DependencyManagementPlugin.class)) { + StringBuilder message = new StringBuilder(); + message.append("\nDuring the build, one or more dependencies that were " + + "declared without a version failed to resolve:\n"); + this.dependenciesWithNoVersion.stream() + .forEach(dependency -> message.append(" " + dependency + "\n")); + message.append("\nDid you forget to apply the " + + "io.spring.dependency-management plugin to the " + project.getName() + + " project?\n"); + logger.warn(message.toString()); + } + } + + private boolean hasNoVersion(ModuleVersionSelector selector) { + String version = selector.getVersion(); + return version == null || version.trim().length() == 0; + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/DependencyManagementPluginActionIntegrationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/DependencyManagementPluginActionIntegrationTests.java index 42b5e87f35a..b22c0a718d9 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/DependencyManagementPluginActionIntegrationTests.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/DependencyManagementPluginActionIntegrationTests.java @@ -16,11 +16,16 @@ package org.springframework.boot.gradle.plugin; +import java.io.File; +import java.io.IOException; + +import org.gradle.testkit.runner.BuildResult; import org.gradle.testkit.runner.TaskOutcome; import org.junit.Rule; import org.junit.Test; import org.springframework.boot.gradle.testkit.GradleBuild; +import org.springframework.util.FileSystemUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -50,4 +55,23 @@ public class DependencyManagementPluginActionIntegrationTests { .isEqualTo(TaskOutcome.SUCCESS); } + @Test + public void helpfulErrorWhenVersionlessDependencyFailsToResolve() throws IOException { + File examplePackage = new File(this.gradleBuild.getProjectDir(), + "src/main/java/com/example"); + examplePackage.mkdirs(); + FileSystemUtils.copyRecursively(new File("src/test/java/com/example"), + examplePackage); + BuildResult result = this.gradleBuild.buildAndFail("compileJava"); + assertThat(result.task(":compileJava").getOutcome()) + .isEqualTo(TaskOutcome.FAILED); + String output = result.getOutput(); + assertThat(output).contains("During the build, one or more dependencies that " + + "were declared without a version failed to resolve:"); + assertThat(output).contains("org.springframework.boot:spring-boot-starter-web:"); + assertThat(output).contains("Did you forget to apply the " + + "io.spring.dependency-management plugin to the " + + this.gradleBuild.getProjectDir().getName() + " project?"); + } + } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java index 7d5f5eb6c0f..cc8b8cb3898 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java @@ -126,22 +126,35 @@ public class GradleBuild implements TestRule { public BuildResult build(String... arguments) { try { - Files.copy(new File(this.script).toPath(), - new File(this.projectDir, "build.gradle").toPath(), - StandardCopyOption.REPLACE_EXISTING); - GradleRunner gradleRunner = GradleRunner.create() - .withProjectDir(this.projectDir).forwardOutput(); - List allArguments = new ArrayList(); - allArguments.add("-PpluginClasspath=" + pluginClasspath()); - allArguments.add("-PbootVersion=" + getBootVersion()); - allArguments.addAll(Arrays.asList(arguments)); - return gradleRunner.withArguments(allArguments).build(); + return prepareRunner(arguments).build(); } catch (Exception ex) { throw new RuntimeException(ex); } } + public BuildResult buildAndFail(String... arguments) { + try { + return prepareRunner(arguments).buildAndFail(); + } + catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + public GradleRunner prepareRunner(String... arguments) throws IOException { + Files.copy(new File(this.script).toPath(), + new File(this.projectDir, "build.gradle").toPath(), + StandardCopyOption.REPLACE_EXISTING); + GradleRunner gradleRunner = GradleRunner.create().withProjectDir(this.projectDir) + .forwardOutput(); + List allArguments = new ArrayList(); + allArguments.add("-PpluginClasspath=" + pluginClasspath()); + allArguments.add("-PbootVersion=" + getBootVersion()); + allArguments.addAll(Arrays.asList(arguments)); + return gradleRunner.withArguments(allArguments); + } + public File getProjectDir() { return this.projectDir; } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/DependencyManagementPluginActionIntegrationTests-helpfulErrorWhenVersionlessDependencyFailsToResolve.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/DependencyManagementPluginActionIntegrationTests-helpfulErrorWhenVersionlessDependencyFailsToResolve.gradle new file mode 100644 index 00000000000..2aeaadbc4a2 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/DependencyManagementPluginActionIntegrationTests-helpfulErrorWhenVersionlessDependencyFailsToResolve.gradle @@ -0,0 +1,16 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'java' +apply plugin: 'org.springframework.boot' + +repositories { + mavenLocal() +} + +dependencies { + compile 'org.springframework.boot:spring-boot-starter-web' +} From b9b402e3db05dd5e737e578be15a59f1d4e618f7 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 30 Mar 2017 12:06:10 +0100 Subject: [PATCH 22/30] Rework BuildInfo to separate task and build info properties Previously, the properties that applied to the BuildInfo task itself and those that would be written into the build-info.properties file were all configured on BuildInfo directly. This lack of separation could be confusing. This commit rework BuildInfo to separate the task's own properties from those that are written into the build-info.properties file. The task has also been updated so that changes to a project's properties made after the task has been configured are reflected in the build info properties. --- .../boot/gradle/dsl/SpringBootExtension.java | 9 +- .../gradle/tasks/buildinfo/BuildInfo.java | 101 ++++----- .../tasks/buildinfo/BuildInfoProperties.java | 212 ++++++++++++++++++ .../buildinfo/BuildInfoIntegrationTests.java | 53 ++++- .../tasks/buildinfo/BuildInfoTests.java | 136 +++++++++++ ...tegrationTests-additionalProperties.gradle | 6 +- ...dInfoIntegrationTests-defaultValues.gradle | 11 + .../BuildInfoIntegrationTests.gradle | 12 +- 8 files changed, 468 insertions(+), 72 deletions(-) create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoProperties.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoTests.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests-defaultValues.gradle diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/dsl/SpringBootExtension.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/dsl/SpringBootExtension.java index 8cbb99bc6cc..468dd12303c 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/dsl/SpringBootExtension.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/dsl/SpringBootExtension.java @@ -27,6 +27,7 @@ import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.bundling.Jar; import org.springframework.boot.gradle.tasks.buildinfo.BuildInfo; +import org.springframework.boot.gradle.tasks.buildinfo.BuildInfoProperties; /** * Entry point to Spring Boot's Gradle DSL. @@ -76,8 +77,12 @@ public class SpringBootExtension { this.project.getPlugins().withType(JavaPlugin.class, plugin -> { this.project.getTasks().getByName(JavaPlugin.CLASSES_TASK_NAME) .dependsOn(bootBuildInfo); - bootBuildInfo.getConventionMapping().map("projectArtifact", - (Callable) () -> determineArtifactBaseName()); + this.project.afterEvaluate(evaluated -> { + BuildInfoProperties properties = bootBuildInfo.getProperties(); + if (properties.getArtifact() == null) { + properties.setArtifact(determineArtifactBaseName()); + } + }); bootBuildInfo.getConventionMapping() .map("destinationDir", (Callable) () -> new File( diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfo.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfo.java index 1f4239e7020..273f7600e70 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfo.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfo.java @@ -22,6 +22,8 @@ import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; +import org.gradle.api.Action; +import org.gradle.api.Project; import org.gradle.api.Task; import org.gradle.api.internal.ConventionTask; import org.gradle.api.tasks.Input; @@ -34,92 +36,77 @@ import org.springframework.boot.loader.tools.BuildPropertiesWriter.ProjectDetail /** * {@link Task} for generating a {@code build-info.properties} file from a - * {@code Project}. The {@link #setDestinationDir destination dir} and - * {@link #setProjectArtifact project artifact} must be configured before execution. + * {@code Project}. * * @author Andy Wilkinson */ public class BuildInfo extends ConventionTask { - private File destinationDir; - - private String projectGroup = getProject().getGroup().toString(); - - private String projectArtifact; - - private String projectVersion = getProject().getVersion().toString(); + private final BuildInfoProperties properties = new BuildInfoProperties(getProject()); - private String projectName = getProject().getName(); - - private Map additionalProperties = new HashMap<>(); + private File destinationDir; + /** + * Generates the {@code build-info.properties} file in the configured + * {@link #setDestinationDir(File) destination}. + */ @TaskAction public void generateBuildProperties() { try { new BuildPropertiesWriter( new File(getDestinationDir(), "build-info.properties")) - .writeBuildProperties(new ProjectDetails(this.projectGroup, - getProjectArtifact(), this.projectVersion, - this.projectName, - coerceToStringValues(this.additionalProperties))); + .writeBuildProperties(new ProjectDetails( + this.properties.getGroup(), + this.properties.getArtifact() == null ? "unspecified" + : this.properties.getArtifact(), + this.properties.getVersion(), + this.properties.getName(), coerceToStringValues( + this.properties.getAdditional()))); } catch (IOException ex) { throw new TaskExecutionException(this, ex); } } - @Input - public String getProjectGroup() { - return this.projectGroup; - } - - public void setProjectGroup(String projectGroup) { - this.projectGroup = projectGroup; - } - - @Input - public String getProjectArtifact() { - return this.projectArtifact; - } - - public void setProjectArtifact(String projectArtifact) { - this.projectArtifact = projectArtifact; - } - - @Input - public String getProjectVersion() { - return this.projectVersion; - } - - public void setProjectVersion(String projectVersion) { - this.projectVersion = projectVersion; - } - - @Input - public String getProjectName() { - return this.projectName; - } - - public void setProjectName(String projectName) { - this.projectName = projectName; - } - + /** + * Returns the directory to which the {@code build-info.properties} file will be + * written. Defaults to the {@link Project#getBuildDir() Project's build directory}. + * + * @return the destination directory + */ @OutputDirectory public File getDestinationDir() { - return this.destinationDir; + return this.destinationDir != null ? this.destinationDir + : getProject().getBuildDir(); } + /** + * Sets the directory to which the {@code build-info.properties} file will be written. + * + * @param destinationDir the destination directory + */ public void setDestinationDir(File destinationDir) { this.destinationDir = destinationDir; } + /** + * Returns the {@link BuildInfoProperties properties} that will be included in the + * {@code build-info.properties} file. + * + * @return the properties + */ @Input - public Map getAdditionalProperties() { - return this.additionalProperties; + public BuildInfoProperties getProperties() { + return this.properties; } - public void setAdditionalProperties(Map additionalProperties) { - this.additionalProperties = additionalProperties; + /** + * Executes the given {@code action} on the {@link #getProperties()} properties. + * + * @param action the action + */ + public void properties(Action action) { + action.execute(this.properties); } private Map coerceToStringValues(Map input) { diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoProperties.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoProperties.java new file mode 100644 index 00000000000..6cbc243ef05 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoProperties.java @@ -0,0 +1,212 @@ +/* + * Copyright 2012-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.boot.gradle.tasks.buildinfo; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import org.gradle.api.Project; + +/** + * The properties that are written into the {@code build-info.properties} file. + * + * @author Andy Wilkinson + */ +public class BuildInfoProperties implements Serializable { + + private final transient Project project; + + private String group; + + private String artifact; + + private String version; + + private String name; + + private Map additionalProperties = new HashMap<>(); + + BuildInfoProperties(Project project) { + this.project = project; + } + + /** + * Returns the value used for the {@code build.group} property. Defaults to the + * {@link Project#getGroup() Project's group}. + * + * @return the group + */ + public String getGroup() { + return this.group != null ? this.group : this.project.getGroup().toString(); + } + + /** + * Sets the value used for the {@code build.group} property. + * + * @param group the group name + */ + public void setGroup(String group) { + this.group = group; + } + + /** + * Returns the value used for the {@code build.artifact} property. + * + * @return the artifact + */ + public String getArtifact() { + return this.artifact; + } + + /** + * Sets the value used for the {@code build.artifact} property. + * + * @param artifact the artifact + */ + public void setArtifact(String artifact) { + this.artifact = artifact; + } + + /** + * Returns the value used for the {@code build.version} property. Defaults to the + * {@link Project#getVersion() Project's version}. + * + * @return the version + */ + public String getVersion() { + return this.version != null ? this.version : this.project.getVersion().toString(); + } + + /** + * Sets the value used for the {@code build.version} property. + * + * @param version the version + */ + public void setVersion(String version) { + this.version = version; + } + + /** + * Returns the value used for the {@code build.name} property. Defaults to the + * {@link Project#getDisplayName() Project's display name}. + * + * @return the name + */ + public String getName() { + return this.name != null ? this.name : this.project.getName(); + } + + /** + * Sets the value used for the {@code build.name} property. + * + * @param name the name + */ + public void setName(String name) { + this.name = name; + } + + /** + * Returns the additional properties that will be included. When written, the name of + * each additional property is prefixed with {@code build.}. + * + * @return the additional properties + */ + public Map getAdditional() { + return this.additionalProperties; + } + + /** + * Sets the additional properties that will be included. When written, the name of + * each additional property is prefixed with {@code build.}. + * + * @param additionalProperties the additional properties + */ + public void setAdditional(Map additionalProperties) { + this.additionalProperties = additionalProperties; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((this.additionalProperties == null) ? 0 + : this.additionalProperties.hashCode()); + result = prime * result + + ((this.artifact == null) ? 0 : this.artifact.hashCode()); + result = prime * result + ((this.group == null) ? 0 : this.group.hashCode()); + result = prime * result + ((this.name == null) ? 0 : this.name.hashCode()); + result = prime * result + ((this.version == null) ? 0 : this.version.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + BuildInfoProperties other = (BuildInfoProperties) obj; + if (this.additionalProperties == null) { + if (other.additionalProperties != null) { + return false; + } + } + else if (!this.additionalProperties.equals(other.additionalProperties)) { + return false; + } + if (this.artifact == null) { + if (other.artifact != null) { + return false; + } + } + else if (!this.artifact.equals(other.artifact)) { + return false; + } + if (this.group == null) { + if (other.group != null) { + return false; + } + } + else if (!this.group.equals(other.group)) { + return false; + } + if (this.name == null) { + if (other.name != null) { + return false; + } + } + else if (!this.name.equals(other.name)) { + return false; + } + if (this.version == null) { + if (other.version != null) { + return false; + } + } + else if (!this.version.equals(other.version)) { + return false; + } + return true; + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.java index 41603256d10..fac2498efa0 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.java @@ -16,6 +16,11 @@ package org.springframework.boot.gradle.tasks.buildinfo; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.Properties; + import org.gradle.testkit.runner.TaskOutcome; import org.junit.Rule; import org.junit.Test; @@ -34,10 +39,30 @@ public class BuildInfoIntegrationTests { @Rule public final GradleBuild gradleBuild = new GradleBuild(); + @Test + public void defaultValues() { + assertThat(this.gradleBuild.build("buildInfo").task(":buildInfo").getOutcome()) + .isEqualTo(TaskOutcome.SUCCESS); + Properties buildInfoProperties = buildInfoProperties(); + assertThat(buildInfoProperties).containsKey("build.time"); + assertThat(buildInfoProperties).containsEntry("build.artifact", "unspecified"); + assertThat(buildInfoProperties).containsEntry("build.group", ""); + assertThat(buildInfoProperties).containsEntry("build.name", + this.gradleBuild.getProjectDir().getName()); + assertThat(buildInfoProperties).containsEntry("build.version", "unspecified"); + } + @Test public void basicExecution() { assertThat(this.gradleBuild.build("buildInfo").task(":buildInfo").getOutcome()) .isEqualTo(TaskOutcome.SUCCESS); + Properties buildInfoProperties = buildInfoProperties(); + assertThat(buildInfoProperties).containsKey("build.time"); + assertThat(buildInfoProperties).containsEntry("build.artifact", "foo"); + assertThat(buildInfoProperties).containsEntry("build.group", "foo"); + assertThat(buildInfoProperties).containsEntry("build.additional", "foo"); + assertThat(buildInfoProperties).containsEntry("build.name", "foo"); + assertThat(buildInfoProperties).containsEntry("build.version", "1.0"); } @Test @@ -55,34 +80,48 @@ public class BuildInfoIntegrationTests { @Test public void notUpToDateWhenProjectArtifactChanges() { - notUpToDateWithChangeToProperty("buildInfoProjectArtifact"); + notUpToDateWithChangeToProperty("buildInfoArtifact"); } @Test public void notUpToDateWhenProjectGroupChanges() { - notUpToDateWithChangeToProperty("buildInfoProjectGroup"); + notUpToDateWithChangeToProperty("buildInfoGroup"); } @Test public void notUpToDateWhenProjectVersionChanges() { - notUpToDateWithChangeToProperty("buildInfoProjectVersion"); + notUpToDateWithChangeToProperty("buildInfoVersion"); } @Test public void notUpToDateWhenProjectNameChanges() { - notUpToDateWithChangeToProperty("buildInfoProjectName"); + notUpToDateWithChangeToProperty("buildInfoName"); } @Test public void notUpToDateWhenAdditionalPropertyChanges() { - notUpToDateWithChangeToProperty("buildInfoAdditionalProperty"); + notUpToDateWithChangeToProperty("buildInfoAdditional"); } private void notUpToDateWithChangeToProperty(String name) { - assertThat(this.gradleBuild.build("buildInfo").task(":buildInfo").getOutcome()) - .isEqualTo(TaskOutcome.SUCCESS); + assertThat(this.gradleBuild.build("buildInfo", "--stacktrace").task(":buildInfo") + .getOutcome()).isEqualTo(TaskOutcome.SUCCESS); assertThat(this.gradleBuild.build("buildInfo", "-P" + name + "=changed") .task(":buildInfo").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); } + private Properties buildInfoProperties() { + File file = new File(this.gradleBuild.getProjectDir(), + "build/build-info.properties"); + assertThat(file).isFile(); + Properties properties = new Properties(); + try (FileReader reader = new FileReader(file)) { + properties.load(reader); + return properties; + } + catch (IOException ex) { + throw new RuntimeException(ex); + } + } + } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoTests.java new file mode 100644 index 00000000000..f459919e619 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoTests.java @@ -0,0 +1,136 @@ +/* + * Copyright 2012-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.boot.gradle.tasks.buildinfo; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.Properties; + +import org.gradle.api.Project; +import org.gradle.testfixtures.ProjectBuilder; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link BuildInfo}. + * + * @author Andy Wilkinson + */ +public class BuildInfoTests { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Test + public void basicExecution() { + Properties properties = buildInfoProperties(createTask(createProject("test"))); + assertThat(properties).containsKey("build.time"); + assertThat(properties).containsEntry("build.artifact", "unspecified"); + assertThat(properties).containsEntry("build.group", ""); + assertThat(properties).containsEntry("build.name", "test"); + assertThat(properties).containsEntry("build.version", "unspecified"); + } + + @Test + public void customArtifactIsReflectedInProperties() { + BuildInfo task = createTask(createProject("test")); + task.getProperties().setArtifact("custom"); + assertThat(buildInfoProperties(task)).containsEntry("build.artifact", "custom"); + } + + @Test + public void projectGroupIsReflectedInProperties() { + BuildInfo task = createTask(createProject("test")); + task.getProject().setGroup("com.example"); + assertThat(buildInfoProperties(task)).containsEntry("build.group", "com.example"); + } + + @Test + public void customGroupIsReflectedInProperties() { + BuildInfo task = createTask(createProject("test")); + task.getProperties().setGroup("com.example"); + assertThat(buildInfoProperties(task)).containsEntry("build.group", "com.example"); + } + + @Test + public void customNameIsReflectedInProperties() { + BuildInfo task = createTask(createProject("test")); + task.getProperties().setName("Example"); + assertThat(buildInfoProperties(task)).containsEntry("build.name", "Example"); + } + + @Test + public void projectVersionIsReflectedInProperties() { + BuildInfo task = createTask(createProject("test")); + task.getProject().setVersion("1.2.3"); + assertThat(buildInfoProperties(task)).containsEntry("build.version", "1.2.3"); + } + + @Test + public void customVersionIsReflectedInProperties() { + BuildInfo task = createTask(createProject("test")); + task.getProperties().setVersion("2.3.4"); + assertThat(buildInfoProperties(task)).containsEntry("build.version", "2.3.4"); + } + + @Test + public void additionalPropertiesAreReflectedInProperties() { + BuildInfo task = createTask(createProject("test")); + task.getProperties().getAdditional().put("a", "alpha"); + task.getProperties().getAdditional().put("b", "bravo"); + assertThat(buildInfoProperties(task)).containsEntry("build.a", "alpha"); + assertThat(buildInfoProperties(task)).containsEntry("build.b", "bravo"); + } + + private Project createProject(String projectName) { + try { + File projectDir = this.temp.newFolder(projectName); + return ProjectBuilder.builder().withProjectDir(projectDir) + .withName(projectName).build(); + } + catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + private BuildInfo createTask(Project project) { + return project.getTasks().create("testBuildInfo", BuildInfo.class); + } + + private Properties buildInfoProperties(BuildInfo task) { + task.generateBuildProperties(); + return buildInfoProperties( + new File(task.getDestinationDir(), "build-info.properties")); + } + + private Properties buildInfoProperties(File file) { + assertThat(file).isFile(); + Properties properties = new Properties(); + try (FileReader reader = new FileReader(file)) { + properties.load(reader); + return properties; + } + catch (IOException ex) { + throw new RuntimeException(ex); + } + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/dsl/BuildInfoDslIntegrationTests-additionalProperties.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/dsl/BuildInfoDslIntegrationTests-additionalProperties.gradle index 93162070be5..4335ef8445f 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/dsl/BuildInfoDslIntegrationTests-additionalProperties.gradle +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/dsl/BuildInfoDslIntegrationTests-additionalProperties.gradle @@ -12,6 +12,10 @@ version = '1.0' springBoot { buildInfo { - additionalProperties 'a': 'alpha', 'b': 'bravo' + properties { + additionalProperties = [ + 'a': 'alpha', 'b': 'bravo' + ] + } } } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests-defaultValues.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests-defaultValues.gradle new file mode 100644 index 00000000000..2fe8266de8b --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests-defaultValues.gradle @@ -0,0 +1,11 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +def property(String name, Object defaultValue) { + project.hasProperty(name) ? project.getProperty(name) : defaultValue +} + +task buildInfo(type: org.springframework.boot.gradle.tasks.buildinfo.BuildInfo) diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.gradle index 23db1b2d8b5..26ce586d5b5 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.gradle +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.gradle @@ -10,9 +10,11 @@ def property(String name, Object defaultValue) { task buildInfo(type: org.springframework.boot.gradle.tasks.buildinfo.BuildInfo) { destinationDir file(property('buildInfoDestinationDir', project.buildDir)) - projectArtifact property('buildInfoProjectArtifact', 'foo') - projectVersion property('buildInfoProjectVersion', '1.0') - projectGroup property('buildInfoProjectGroup', 'foo') - projectName property('buildInfoProjectName', 'foo') - additionalProperties 'additional': property('buildInfoAdditionalProperty', 'foo') + properties { + artifact = property('buildInfoArtifact', 'foo') + version = property('buildInfoVersion', '1.0') + group = property('buildInfoGroup', 'foo') + name = property('buildInfoName', 'foo') + additional = ['additional': property('buildInfoAdditional', 'foo')] + } } From c1f2f8079bb4c62dda669af934b75b6eb42c9b23 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 30 Mar 2017 12:59:39 +0100 Subject: [PATCH 23/30] Integration test the reaction to the Java plugin being applied --- .../boot/gradle/plugin/JavaPluginAction.java | 4 +- .../JavaPluginActionIntegrationTests.java | 82 +++++++++++++++++++ .../JavaPluginActionIntegrationTests.gradle | 31 +++++++ 3 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/JavaPluginActionIntegrationTests.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/JavaPluginActionIntegrationTests.gradle diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/JavaPluginAction.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/JavaPluginAction.java index 86515e51d77..16e1a3e98ee 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/JavaPluginAction.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/JavaPluginAction.java @@ -100,8 +100,8 @@ final class JavaPluginAction implements PluginApplicationAction { } private void configureUtf8Encoding(Project project) { - project.getTasks().withType(JavaCompile.class, - compile -> compile.doFirst(task -> { + project.afterEvaluate( + evaluated -> evaluated.getTasks().withType(JavaCompile.class, compile -> { if (compile.getOptions().getEncoding() == null) { compile.getOptions().setEncoding("UTF-8"); } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/JavaPluginActionIntegrationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/JavaPluginActionIntegrationTests.java new file mode 100644 index 00000000000..5ae7a016e0b --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/JavaPluginActionIntegrationTests.java @@ -0,0 +1,82 @@ +/* + * Copyright 2012-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.boot.gradle.plugin; + +import org.junit.Rule; +import org.junit.Test; + +import org.springframework.boot.gradle.testkit.GradleBuild; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for {@link WarPluginAction}. + * + * @author Andy Wilkinson + */ +public class JavaPluginActionIntegrationTests { + + @Rule + public GradleBuild gradleBuild = new GradleBuild(); + + @Test + public void noBootJarTaskWithoutJavaPluginApplied() { + assertThat(this.gradleBuild.build("taskExists", "-PtaskName=bootJar").getOutput()) + .contains("bootJar exists = false"); + } + + @Test + public void applyingJavaPluginCreatesBootJarTask() { + assertThat(this.gradleBuild + .build("taskExists", "-PtaskName=bootJar", "-PapplyJavaPlugin") + .getOutput()).contains("bootJar exists = true"); + } + + @Test + public void noBootRunTaskWithoutJavaPluginApplied() { + assertThat(this.gradleBuild.build("taskExists", "-PtaskName=bootRun").getOutput()) + .contains("bootRun exists = false"); + } + + @Test + public void applyingJavaPluginCreatesBootRunTask() { + assertThat(this.gradleBuild + .build("taskExists", "-PtaskName=bootRun", "-PapplyJavaPlugin") + .getOutput()).contains("bootRun exists = true"); + } + + @Test + public void noBootJavaSoftwareComponentWithoutJavaPluginApplied() { + assertThat(this.gradleBuild.build("componentExists", "-PcomponentName=bootJava") + .getOutput()).contains("bootJava exists = false"); + } + + @Test + public void applyingJavaPluginCreatesBootJavaSoftwareComponent() { + assertThat(this.gradleBuild + .build("componentExists", "-PcomponentName=bootJava", "-PapplyJavaPlugin") + .getOutput()).contains("bootJava exists = true"); + } + + @Test + public void javaCompileTasksUseUtf8Encoding() { + assertThat(this.gradleBuild.build("javaCompileEncoding", "-PapplyJavaPlugin") + .getOutput()).contains("compileJava = UTF-8") + .contains("compileTestJava = UTF-8"); + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/JavaPluginActionIntegrationTests.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/JavaPluginActionIntegrationTests.gradle new file mode 100644 index 00000000000..20fb8832f22 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/JavaPluginActionIntegrationTests.gradle @@ -0,0 +1,31 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' + +if (project.hasProperty('applyJavaPlugin')) { + apply plugin: 'java' +} + +task('taskExists') { + doFirst { + println "$taskName exists = ${tasks.findByName(taskName) != null}" + } +} + +task('componentExists') { + doFirst { + println "$componentName exists = ${components.findByName(componentName) != null}" + } +} + +task('javaCompileEncoding') { + doFirst { + tasks.withType(JavaCompile) { + println "$name = ${options.encoding}" + } + } +} From e552975a7aa20bf1accf9e13e72fbd0cfe217d71 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 30 Mar 2017 13:11:51 +0100 Subject: [PATCH 24/30] Integration test the reaction to the War plugin being applied --- .../WarPluginActionIntegrationTests.java | 62 +++++++++++++++++++ .../WarPluginActionIntegrationTests.gradle | 23 +++++++ 2 files changed, 85 insertions(+) create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/WarPluginActionIntegrationTests.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/WarPluginActionIntegrationTests.gradle diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/WarPluginActionIntegrationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/WarPluginActionIntegrationTests.java new file mode 100644 index 00000000000..c9555ed1b1a --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/WarPluginActionIntegrationTests.java @@ -0,0 +1,62 @@ +/* + * Copyright 2012-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.boot.gradle.plugin; + +import org.junit.Rule; +import org.junit.Test; + +import org.springframework.boot.gradle.testkit.GradleBuild; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for {@link JavaPluginAction}. + * + * @author Andy Wilkinson + */ +public class WarPluginActionIntegrationTests { + + @Rule + public GradleBuild gradleBuild = new GradleBuild(); + + @Test + public void noBootWarTaskWithoutWarPluginApplied() { + assertThat(this.gradleBuild.build("taskExists", "-PtaskName=bootWar").getOutput()) + .contains("bootWar exists = false"); + } + + @Test + public void applyingWarPluginCreatesBootWarTask() { + assertThat(this.gradleBuild + .build("taskExists", "-PtaskName=bootWar", "-PapplyWarPlugin") + .getOutput()).contains("bootWar exists = true"); + } + + @Test + public void noBootWebSoftwareComponentWithoutJavaPluginApplied() { + assertThat(this.gradleBuild.build("componentExists", "-PcomponentName=bootWeb") + .getOutput()).contains("bootWeb exists = false"); + } + + @Test + public void applyingJavaPluginCreatesBootWebSoftwareComponent() { + assertThat(this.gradleBuild + .build("componentExists", "-PcomponentName=bootWeb", "-PapplyWarPlugin") + .getOutput()).contains("bootWeb exists = true"); + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/WarPluginActionIntegrationTests.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/WarPluginActionIntegrationTests.gradle new file mode 100644 index 00000000000..9bec2b8c13c --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/WarPluginActionIntegrationTests.gradle @@ -0,0 +1,23 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' + +if (project.hasProperty('applyWarPlugin')) { + apply plugin: 'war' +} + +task('taskExists') { + doFirst { + println "$taskName exists = ${tasks.findByName(taskName) != null}" + } +} + +task('componentExists') { + doFirst { + println "$componentName exists = ${components.findByName(componentName) != null}" + } +} From 915c17b95570cda16eeb75805935fd11d84aea2d Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 30 Mar 2017 14:00:56 +0100 Subject: [PATCH 25/30] Integration test the reaction to the Maven plugin being applied --- .../MavenPluginActionIntegrationTests.java | 42 +++++++++++++++++++ .../MavenPluginActionIntegrationTests.gradle | 24 +++++++++++ 2 files changed, 66 insertions(+) create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/MavenPluginActionIntegrationTests.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/MavenPluginActionIntegrationTests.gradle diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/MavenPluginActionIntegrationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/MavenPluginActionIntegrationTests.java new file mode 100644 index 00000000000..efc756eb555 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/MavenPluginActionIntegrationTests.java @@ -0,0 +1,42 @@ +/* + * Copyright 2012-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.boot.gradle.plugin; + +import org.junit.Rule; +import org.junit.Test; + +import org.springframework.boot.gradle.testkit.GradleBuild; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for {@link MavenPluginAction}. + * + * @author Andy Wilkinson + */ +public class MavenPluginActionIntegrationTests { + + @Rule + public GradleBuild gradleBuild = new GradleBuild(); + + @Test + public void clearsConf2ScopeMappingsOfUploadBootArchivesTask() { + assertThat(this.gradleBuild.build("conf2ScopeMappings").getOutput()) + .contains("Conf2ScopeMappings = 0"); + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/MavenPluginActionIntegrationTests.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/MavenPluginActionIntegrationTests.gradle new file mode 100644 index 00000000000..068722c2fec --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/MavenPluginActionIntegrationTests.gradle @@ -0,0 +1,24 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'java' +apply plugin: 'maven' + +task('conf2ScopeMappings') { + doFirst { + tasks.getByName('uploadBootArchives').repositories.withType(MavenResolver) { + println "Conf2ScopeMappings = ${pom.scopeMappings.mappings.size()}" + } + } +} + +uploadBootArchives { + repositories { + mavenDeployer { + } + } +} From 85267def3e4dab0ab54b712d638181ced78e40d1 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 30 Mar 2017 16:05:53 +0100 Subject: [PATCH 26/30] Integration test the reaction to the Application plugin being applied --- .../spring-boot-gradle-plugin/build.gradle | 1 + ...plicationPluginActionIntegrationTests.java | 156 ++++++++++++++++++ ...sts-tarDistributionForJarCanBeBuilt.gradle | 13 ++ ...sts-tarDistributionForWarCanBeBuilt.gradle | 13 ++ ...sts-zipDistributionForJarCanBeBuilt.gradle | 13 ++ ...sts-zipDistributionForWarCanBeBuilt.gradle | 13 ++ ...icationPluginActionIntegrationTests.gradle | 33 ++++ 7 files changed, 242 insertions(+) create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests-tarDistributionForJarCanBeBuilt.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests-tarDistributionForWarCanBeBuilt.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests-zipDistributionForJarCanBeBuilt.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests-zipDistributionForWarCanBeBuilt.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests.gradle diff --git a/spring-boot-tools/spring-boot-gradle-plugin/build.gradle b/spring-boot-tools/spring-boot-gradle-plugin/build.gradle index 5f3c795b5ac..01b42fef767 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/build.gradle +++ b/spring-boot-tools/spring-boot-gradle-plugin/build.gradle @@ -37,6 +37,7 @@ dependencies { compile localGroovy() compile gradleApi() testCompile gradleTestKit() + testCompile 'org.apache.commons:commons-compress:1.13' } jar { diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests.java new file mode 100644 index 00000000000..6a9dd6c1f79 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests.java @@ -0,0 +1,156 @@ +/* + * Copyright 2012-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.boot.gradle.plugin; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; +import org.gradle.testkit.runner.TaskOutcome; +import org.junit.Rule; +import org.junit.Test; + +import org.springframework.boot.gradle.testkit.GradleBuild; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for {@link ApplicationPluginAction}. + * + * @author Andy Wilkinson + */ +public class ApplicationPluginActionIntegrationTests { + + @Rule + public GradleBuild gradleBuild = new GradleBuild(); + + @Test + public void noBootDistributionWithoutApplicationPluginApplied() { + assertThat(this.gradleBuild.build("distributionExists", "-PdistributionName=boot") + .getOutput()).contains("boot exists = false"); + } + + @Test + public void applyingApplicationPluginCreatesBootDistribution() { + assertThat(this.gradleBuild.build("distributionExists", "-PdistributionName=boot", + "-PapplyApplicationPlugin").getOutput()).contains("boot exists = true"); + } + + @Test + public void noBootStartScriptsTaskWithoutApplicationPluginApplied() { + assertThat(this.gradleBuild.build("taskExists", "-PtaskName=bootStartScripts") + .getOutput()).contains("bootStartScripts exists = false"); + } + + @Test + public void applyingApplictionPluginCreatesBootStartScriptsTask() { + assertThat(this.gradleBuild.build("taskExists", "-PtaskName=bootStartScripts", + "-PapplyApplicationPlugin").getOutput()) + .contains("bootStartScripts exists = true"); + } + + @Test + public void zipDistributionForJarCanBeBuilt() throws IOException { + assertThat( + this.gradleBuild.build("bootDistZip").task(":bootDistZip").getOutcome()) + .isEqualTo(TaskOutcome.SUCCESS); + String name = this.gradleBuild.getProjectDir().getName(); + File distribution = new File(this.gradleBuild.getProjectDir(), + "build/distributions/" + name + "-boot.zip"); + assertThat(distribution).isFile(); + assertThat(zipEntryNames(distribution)).containsExactlyInAnyOrder(name + "-boot/", + name + "-boot/lib/", name + "-boot/lib/" + name + ".jar", + name + "-boot/bin/", name + "-boot/bin/" + name, + name + "-boot/bin/" + name + ".bat"); + } + + @Test + public void tarDistributionForJarCanBeBuilt() throws IOException { + assertThat( + this.gradleBuild.build("bootDistTar").task(":bootDistTar").getOutcome()) + .isEqualTo(TaskOutcome.SUCCESS); + String name = this.gradleBuild.getProjectDir().getName(); + File distribution = new File(this.gradleBuild.getProjectDir(), + "build/distributions/" + name + "-boot.tar"); + assertThat(distribution).isFile(); + assertThat(tarEntryNames(distribution)).containsExactlyInAnyOrder(name + "-boot/", + name + "-boot/lib/", name + "-boot/lib/" + name + ".jar", + name + "-boot/bin/", name + "-boot/bin/" + name, + name + "-boot/bin/" + name + ".bat"); + } + + @Test + public void zipDistributionForWarCanBeBuilt() throws IOException { + assertThat( + this.gradleBuild.build("bootDistZip").task(":bootDistZip").getOutcome()) + .isEqualTo(TaskOutcome.SUCCESS); + String name = this.gradleBuild.getProjectDir().getName(); + File distribution = new File(this.gradleBuild.getProjectDir(), + "build/distributions/" + name + "-boot.zip"); + assertThat(distribution).isFile(); + assertThat(zipEntryNames(distribution)).containsExactlyInAnyOrder(name + "-boot/", + name + "-boot/lib/", name + "-boot/lib/" + name + ".war", + name + "-boot/bin/", name + "-boot/bin/" + name, + name + "-boot/bin/" + name + ".bat"); + } + + @Test + public void tarDistributionForWarCanBeBuilt() throws IOException { + assertThat( + this.gradleBuild.build("bootDistTar").task(":bootDistTar").getOutcome()) + .isEqualTo(TaskOutcome.SUCCESS); + String name = this.gradleBuild.getProjectDir().getName(); + File distribution = new File(this.gradleBuild.getProjectDir(), + "build/distributions/" + name + "-boot.tar"); + assertThat(distribution).isFile(); + assertThat(tarEntryNames(distribution)).containsExactlyInAnyOrder(name + "-boot/", + name + "-boot/lib/", name + "-boot/lib/" + name + ".war", + name + "-boot/bin/", name + "-boot/bin/" + name, + name + "-boot/bin/" + name + ".bat"); + } + + private List zipEntryNames(File distribution) throws IOException { + List entryNames = new ArrayList<>(); + try (ZipFile zipFile = new ZipFile(distribution)) { + Enumeration entries = zipFile.entries(); + while (entries.hasMoreElements()) { + entryNames.add(entries.nextElement().getName()); + } + } + return entryNames; + } + + private List tarEntryNames(File distribution) throws IOException { + List entryNames = new ArrayList<>(); + try (TarArchiveInputStream input = new TarArchiveInputStream( + new FileInputStream(distribution))) { + TarArchiveEntry entry; + while ((entry = input.getNextTarEntry()) != null) { + entryNames.add(entry.getName()); + } + } + return entryNames; + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests-tarDistributionForJarCanBeBuilt.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests-tarDistributionForJarCanBeBuilt.gradle new file mode 100644 index 00000000000..ed26697d0ef --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests-tarDistributionForJarCanBeBuilt.gradle @@ -0,0 +1,13 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'application' +apply plugin: 'java' + +bootJar { + mainClass = 'com.example.ExampleApplication' +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests-tarDistributionForWarCanBeBuilt.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests-tarDistributionForWarCanBeBuilt.gradle new file mode 100644 index 00000000000..aa4a2a76334 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests-tarDistributionForWarCanBeBuilt.gradle @@ -0,0 +1,13 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'application' +apply plugin: 'war' + +bootWar { + mainClass = 'com.example.ExampleApplication' +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests-zipDistributionForJarCanBeBuilt.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests-zipDistributionForJarCanBeBuilt.gradle new file mode 100644 index 00000000000..ed26697d0ef --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests-zipDistributionForJarCanBeBuilt.gradle @@ -0,0 +1,13 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'application' +apply plugin: 'java' + +bootJar { + mainClass = 'com.example.ExampleApplication' +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests-zipDistributionForWarCanBeBuilt.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests-zipDistributionForWarCanBeBuilt.gradle new file mode 100644 index 00000000000..aa4a2a76334 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests-zipDistributionForWarCanBeBuilt.gradle @@ -0,0 +1,13 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'application' +apply plugin: 'war' + +bootWar { + mainClass = 'com.example.ExampleApplication' +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests.gradle new file mode 100644 index 00000000000..18509a038c3 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests.gradle @@ -0,0 +1,33 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' + +if (project.hasProperty('applyApplicationPlugin')) { + apply plugin: 'application' +} + +task('taskExists') { + doFirst { + println "$taskName exists = ${tasks.findByName(taskName) != null}" + } +} + +task('distributionExists') { + doFirst { + boolean found = project.hasProperty('distributions') && + distributions.findByName(distributionName) != null + println "$distributionName exists = $found" + } +} + +task('javaCompileEncoding') { + doFirst { + tasks.withType(JavaCompile) { + println "$name = ${options.encoding}" + } + } +} From b6a4056e9523ac1bd4d011d8f6c34888c1a0dc6f Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 3 Apr 2017 15:18:17 +0100 Subject: [PATCH 27/30] Remove dependency management for Gradle that is no longer used --- spring-boot-dependencies/pom.xml | 1 - spring-boot-parent/pom.xml | 45 -------------------------------- 2 files changed, 46 deletions(-) diff --git a/spring-boot-dependencies/pom.xml b/spring-boot-dependencies/pom.xml index 7409ed41901..3821310b094 100644 --- a/spring-boot-dependencies/pom.xml +++ b/spring-boot-dependencies/pom.xml @@ -78,7 +78,6 @@ 2.3.25-incubating 2.4.4 3.0.0 - 3.4 2.4.10 2.8.0 1.4.194 diff --git a/spring-boot-parent/pom.xml b/spring-boot-parent/pom.xml index 4949c3ba7eb..6b5998382c2 100644 --- a/spring-boot-parent/pom.xml +++ b/spring-boot-parent/pom.xml @@ -199,51 +199,6 @@ aether-util ${aether.version} - - org.gradle - gradle-base-services - ${gradle.version} - - - org.gradle - gradle-base-services-groovy - ${gradle.version} - - - org.gradle - gradle-core - ${gradle.version} - - - org.gradle - gradle-language-java - ${gradle.version} - - - org.gradle - gradle-language-jvm - ${gradle.version} - - - org.gradle - gradle-maven - ${gradle.version} - - - org.gradle - gradle-platform-jvm - ${gradle.version} - - - org.gradle - gradle-plugins - ${gradle.version} - - - org.gradle - gradle-process-services - ${gradle.version} - org.jetbrains.kotlin kotlin-runtime From d43b1ae3a5144517e3afb5a49311f2cd1c492b7f Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 3 Apr 2017 15:41:48 +0100 Subject: [PATCH 28/30] Polish the Gradle plugin's javadoc --- .../boot/gradle/dsl/SpringBootExtension.java | 1 + .../plugin/PluginApplicationAction.java | 6 ++++ .../boot/gradle/plugin/SpringBootPlugin.java | 10 ++++++ .../UnresolvedDependenciesAnalyzer.java | 2 +- .../gradle/tasks/bundling/BootArchive.java | 1 + .../boot/gradle/tasks/bundling/BootJar.java | 4 +++ .../boot/gradle/tasks/bundling/BootWar.java | 15 ++++++-- .../bundling/LaunchScriptConfiguration.java | 35 +++++++++++++++++++ .../gradle/tasks/bundling/ZipCompression.java | 1 + .../boot/gradle/tasks/run/BootRun.java | 1 + 10 files changed, 73 insertions(+), 3 deletions(-) diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/dsl/SpringBootExtension.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/dsl/SpringBootExtension.java index 468dd12303c..04896a63853 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/dsl/SpringBootExtension.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/dsl/SpringBootExtension.java @@ -33,6 +33,7 @@ import org.springframework.boot.gradle.tasks.buildinfo.BuildInfoProperties; * Entry point to Spring Boot's Gradle DSL. * * @author Andy Wilkinson + * @since 2.0.0 */ public class SpringBootExtension { diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/PluginApplicationAction.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/PluginApplicationAction.java index 5cfb9dec7f6..a02de18610b 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/PluginApplicationAction.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/PluginApplicationAction.java @@ -28,6 +28,12 @@ import org.gradle.api.Project; */ interface PluginApplicationAction extends Action { + /** + * The class of the {@code Plugin} that, when applied, will trigger the execution of + * this action. + * + * @return the plugin class + */ Class> getPluginClass(); } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java index ba5ed878076..8d6bf837f29 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java @@ -39,26 +39,36 @@ public class SpringBootPlugin implements Plugin { /** * The name of the {@link Configuration} that contains Spring Boot archives. + * + * @since 2.0.0 */ public static final String BOOT_ARCHIVES_CONFIURATION_NAME = "bootArchives"; /** * The name of the {@link SoftwareComponent} for a Spring Boot Java application. + * + * @since 2.0.0 */ public static final String BOOT_JAVA_SOFTWARE_COMPONENT_NAME = "bootJava"; /** * The name of the {@link SoftwareComponent} for a Spring Boot Web application. + * + * @since 2.0.0 */ public static final String BOOT_WEB_SOFTWARE_COMPONENT_NAME = "bootWeb"; /** * The name of the default {@link BootJar} task. + * + * @since 2.0.0 */ public static final String BOOT_JAR_TASK_NAME = "bootJar"; /** * The name of the default {@link BootWar} task. + * + * @since 2.0.0 */ public static final String BOOT_WAR_TASK_NAME = "bootWar"; diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/UnresolvedDependenciesAnalyzer.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/UnresolvedDependenciesAnalyzer.java index 621d01d45b2..b56ab5cea0a 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/UnresolvedDependenciesAnalyzer.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/UnresolvedDependenciesAnalyzer.java @@ -40,7 +40,7 @@ class UnresolvedDependenciesAnalyzer { private Set dependenciesWithNoVersion = new HashSet<>(); - public void analyze(Set unresolvedDependencies) { + void analyze(Set unresolvedDependencies) { this.dependenciesWithNoVersion = unresolvedDependencies.stream() .map(unresolvedDependency -> unresolvedDependency.getSelector()) .filter(this::hasNoVersion).collect(Collectors.toSet()); diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootArchive.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootArchive.java index 2305491ac07..88996acd780 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootArchive.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootArchive.java @@ -30,6 +30,7 @@ import org.gradle.api.tasks.Optional; * A Spring Boot "fat" archive task. * * @author Andy Wilkinson + * @since 2.0.0 */ public interface BootArchive extends Task { diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootJar.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootJar.java index 15682398f7d..74befe9eb67 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootJar.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootJar.java @@ -33,6 +33,7 @@ import org.gradle.api.tasks.bundling.Jar; * A custom {@link Jar} task that produces a Spring Boot executable jar. * * @author Andy Wilkinson + * @since 2.0.0 */ public class BootJar extends Jar implements BootArchive { @@ -43,6 +44,9 @@ public class BootJar extends Jar implements BootArchive { private String mainClass; + /** + * Creates a new {@code BootJar} task. + */ public BootJar() { CopySpec bootInf = getRootSpec().addChildBeforeSpec(getMainSpec()) .into("BOOT-INF"); diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootWar.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootWar.java index 8bf8ff15ba9..3b704135db3 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootWar.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootWar.java @@ -34,6 +34,7 @@ import org.gradle.api.tasks.bundling.War; * A custom {@link War} task that produces a Spring Boot executable war. * * @author Andy Wilkinson + * @since 2.0.0 */ public class BootWar extends War implements BootArchive { @@ -44,6 +45,9 @@ public class BootWar extends War implements BootArchive { private FileCollection providedClasspath; + /** + * Creates a new {@code BootWar} task. + */ public BootWar() { getWebInf().into("lib-provided", copySpec -> copySpec @@ -92,14 +96,21 @@ public class BootWar extends War implements BootArchive { action.execute(getLaunchScript()); } + /** + * Returns the provided classpath, the contents of which will be included in the + * {@code WEB-INF/lib-provided} directory of the war. + * + * @return the provided classpath + */ @Optional public FileCollection getProvidedClasspath() { return this.providedClasspath; } /** - * Adds files to the provided classpath to include in the war. The given - * {@code classpath} are evaluated as per {@link Project#files(Object...)}. + * Adds files to the provided classpath to include in the {@code WEB-INF/lib-provided} + * directory of the war. The given {@code classpath} are evaluated as per + * {@link Project#files(Object...)}. * * @param classpath the additions to the classpath */ diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/LaunchScriptConfiguration.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/LaunchScriptConfiguration.java index 3e31f768425..40ee454fb5a 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/LaunchScriptConfiguration.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/LaunchScriptConfiguration.java @@ -28,6 +28,7 @@ import org.springframework.boot.loader.tools.FileUtils; * Encapsulates the configuration of the launch script for an executable jar or war. * * @author Andy Wilkinson + * @since 2.0.0 */ public class LaunchScriptConfiguration implements Serializable { @@ -37,26 +38,60 @@ public class LaunchScriptConfiguration implements Serializable { private File script; + /** + * Returns whether the launch script is included. Defaults to {@code false}. + * + * @return {@code true} is the script is included, otherwise {@code false}. + */ public boolean isIncluded() { return this.included; } + /** + * Sets whether the launch script is included. Defaults to {@code false}. + * + * @param included {@code true} is the script is included, otherwise {@code false}. + */ public void setIncluded(boolean included) { this.included = included; } + /** + * Returns the properties that are applied to the launch script when it's being + * including in the executable archive. + * + * @return the properties + */ public Map getProperties() { return this.properties; } + /** + * Sets the properties that are applied to the launch script when it's being including + * in the executable archive. + * + * @param properties the properties + */ public void properties(Map properties) { this.properties.putAll(properties); } + /** + * Returns the script {@link File} that will be included in the executable archive. + * When {@code null}, the default launch script will be used. + * + * @return the script file + */ public File getScript() { return this.script; } + /** + * Sets the script {@link File} that will be included in the executable archive. When + * {@code null}, the default launch script will be used. + * + * @param script the script file + */ public void setScript(File script) { this.script = script; } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/ZipCompression.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/ZipCompression.java index 9aa624732b1..782cddedd4c 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/ZipCompression.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/ZipCompression.java @@ -22,6 +22,7 @@ import java.util.zip.ZipEntry; * An enumeration of supported compression options for an entry in a ZIP archive. * * @author Andy Wilkinson + * @since 2.0.0 */ public enum ZipCompression { diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/run/BootRun.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/run/BootRun.java index 2a0b41cc0e5..07a463ae7ff 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/run/BootRun.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/run/BootRun.java @@ -25,6 +25,7 @@ import org.gradle.api.tasks.SourceSetOutput; * Custom {@link JavaExec} task for running a Spring Boot application. * * @author Andy Wilkinson + * @since 2.0.0 */ public class BootRun extends JavaExec { From 47c0c3c0ef3901add530e8f310fec6af920586ea Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 3 Apr 2017 17:05:08 +0100 Subject: [PATCH 29/30] Update samples with Gradle configuration to align with new plugin --- .../spring-boot-sample-actuator/build.gradle | 21 +---- .../spring-boot-sample-custom-layout/pom.xml | 24 ----- .../src/it/custom/build.gradle | 28 ------ .../src/it/default/build.gradle | 24 ----- .../src/test/java/sample/layout/GradleIT.java | 90 ------------------- .../src/test/java/sample/layout/Verify.java | 45 ---------- .../spring-boot-sample-logback/build.gradle | 17 +--- .../spring-boot-sample-profile/build.gradle | 25 +----- .../spring-boot-sample-profile/pom.xml | 17 ---- .../spring-boot-sample-simple/build.gradle | 16 +--- .../build.gradle | 17 +--- .../spring-boot-sample-web-ui/build.gradle | 15 +--- 12 files changed, 18 insertions(+), 321 deletions(-) delete mode 100644 spring-boot-samples/spring-boot-sample-custom-layout/src/it/custom/build.gradle delete mode 100644 spring-boot-samples/spring-boot-sample-custom-layout/src/it/default/build.gradle delete mode 100644 spring-boot-samples/spring-boot-sample-custom-layout/src/test/java/sample/layout/GradleIT.java delete mode 100644 spring-boot-samples/spring-boot-sample-custom-layout/src/test/java/sample/layout/Verify.java diff --git a/spring-boot-samples/spring-boot-sample-actuator/build.gradle b/spring-boot-samples/spring-boot-sample-actuator/build.gradle index 3a3930054c6..0d4912053b0 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/build.gradle +++ b/spring-boot-samples/spring-boot-sample-actuator/build.gradle @@ -19,10 +19,7 @@ apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'idea' apply plugin: 'org.springframework.boot' - -jar { - baseName = 'spring-boot-sample-actuator' -} +apply plugin: 'io.spring.dependency-management' group = 'org.springframework.boot' version = springBootVersion @@ -37,10 +34,6 @@ repositories { } dependencies { - configurations { - insecure.exclude module: 'spring-boot-starter-security' - } - compile("org.springframework.boot:spring-boot-starter-actuator") compile("org.springframework.boot:spring-boot-starter-jdbc") compile("org.springframework.boot:spring-boot-starter-security") @@ -50,18 +43,6 @@ dependencies { compileOnly('org.springframework.boot:spring-boot-configuration-processor') testCompile("org.springframework.boot:spring-boot-starter-test") - - insecure configurations.runtime -} - -// Slightly odd requirement (package a jar file as an insecure app, excluding Spring Security) -// just to demonstrate the "customConfiguration" feature of the Boot gradle plugin. -springBoot { - customConfiguration = "insecure" -} - -task wrapper(type: Wrapper) { - gradleVersion = '1.6' } springBoot { diff --git a/spring-boot-samples/spring-boot-sample-custom-layout/pom.xml b/spring-boot-samples/spring-boot-sample-custom-layout/pom.xml index b118522f106..b545cd9ea7f 100644 --- a/spring-boot-samples/spring-boot-sample-custom-layout/pom.xml +++ b/spring-boot-samples/spring-boot-sample-custom-layout/pom.xml @@ -25,18 +25,6 @@ org.springframework.boot spring-boot-loader-tools - - - org.springframework.boot - spring-boot-starter-test - test - - - org.gradle - gradle-tooling-api - ${gradle.version} - test - @@ -96,16 +84,4 @@ - - - gradle - http://repo.gradle.org/gradle/libs-releases-local - - true - - - false - - - diff --git a/spring-boot-samples/spring-boot-sample-custom-layout/src/it/custom/build.gradle b/spring-boot-samples/spring-boot-sample-custom-layout/src/it/custom/build.gradle deleted file mode 100644 index 4bad1c3da16..00000000000 --- a/spring-boot-samples/spring-boot-sample-custom-layout/src/it/custom/build.gradle +++ /dev/null @@ -1,28 +0,0 @@ -buildscript { - repositories { - flatDir { - dirs '../..' - } - mavenLocal() - } - dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:${project.bootVersion}" - classpath "org.springframework.boot:spring-boot-sample-custom-layout:${project.bootVersion}" - } -} - -repositories { - mavenLocal() - mavenCentral() -} - -apply plugin: 'java' -apply plugin: 'org.springframework.boot' - -springBoot { - layoutFactory = new sample.layout.SampleLayoutFactory('custom') -} - -dependencies { - compile 'org.springframework.boot:spring-boot-starter' -} diff --git a/spring-boot-samples/spring-boot-sample-custom-layout/src/it/default/build.gradle b/spring-boot-samples/spring-boot-sample-custom-layout/src/it/default/build.gradle deleted file mode 100644 index 7c10b209ff0..00000000000 --- a/spring-boot-samples/spring-boot-sample-custom-layout/src/it/default/build.gradle +++ /dev/null @@ -1,24 +0,0 @@ -buildscript { - repositories { - flatDir { - dirs '../..' - } - mavenLocal() - } - dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:${project.bootVersion}" - classpath "org.springframework.boot:spring-boot-sample-custom-layout:${project.bootVersion}" - } -} - -repositories { - mavenLocal() - mavenCentral() -} - -apply plugin: 'java' -apply plugin: 'org.springframework.boot' - -dependencies { - compile 'org.springframework.boot:spring-boot-starter' -} diff --git a/spring-boot-samples/spring-boot-sample-custom-layout/src/test/java/sample/layout/GradleIT.java b/spring-boot-samples/spring-boot-sample-custom-layout/src/test/java/sample/layout/GradleIT.java deleted file mode 100644 index b488064a21e..00000000000 --- a/spring-boot-samples/spring-boot-sample-custom-layout/src/test/java/sample/layout/GradleIT.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2012-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 sample.layout; - -import java.io.File; -import java.io.FileReader; - -import javax.xml.xpath.XPath; -import javax.xml.xpath.XPathExpression; -import javax.xml.xpath.XPathFactory; - -import org.gradle.tooling.GradleConnector; -import org.gradle.tooling.ProjectConnection; -import org.gradle.tooling.internal.consumer.DefaultGradleConnector; -import org.junit.Test; -import org.xml.sax.InputSource; - -import org.springframework.util.FileCopyUtils; - -public class GradleIT { - - @Test - public void sampleDefault() throws Exception { - test("default", "sample"); - } - - @Test - public void sampleCustom() throws Exception { - test("custom", "custom"); - } - - private void test(String name, String expected) throws Exception { - File projectDirectory = new File("target/gradleit/" + name); - File javaDirectory = new File( - "target/gradleit/" + name + "/src/main/java/org/test/"); - projectDirectory.mkdirs(); - javaDirectory.mkdirs(); - File script = new File(projectDirectory, "build.gradle"); - FileCopyUtils.copy(new File("src/it/" + name + "/build.gradle"), script); - FileCopyUtils.copy( - new File("src/it/" + name - + "/src/main/java/org/test/SampleApplication.java"), - new File(javaDirectory, "SampleApplication.java")); - GradleConnector gradleConnector = GradleConnector.newConnector(); - gradleConnector.useGradleVersion("2.9"); - ((DefaultGradleConnector) gradleConnector).embedded(true); - ProjectConnection project = gradleConnector.forProjectDirectory(projectDirectory) - .connect(); - project.newBuild().forTasks("clean", "build").setStandardOutput(System.out) - .setStandardError(System.err) - .withArguments("-PbootVersion=" + getBootVersion()).run(); - Verify.verify( - new File("target/gradleit/" + name + "/build/libs/" + name + ".jar"), - expected); - } - - public static String getBootVersion() { - return evaluateExpression( - "/*[local-name()='project']/*[local-name()='parent']/*[local-name()='version']" - + "/text()"); - } - - private static String evaluateExpression(String expression) { - try { - XPathFactory xPathFactory = XPathFactory.newInstance(); - XPath xpath = xPathFactory.newXPath(); - XPathExpression expr = xpath.compile(expression); - String version = expr.evaluate(new InputSource(new FileReader("pom.xml"))); - return version; - } - catch (Exception ex) { - throw new IllegalStateException("Failed to evaluate expression", ex); - } - } - -} diff --git a/spring-boot-samples/spring-boot-sample-custom-layout/src/test/java/sample/layout/Verify.java b/spring-boot-samples/spring-boot-sample-custom-layout/src/test/java/sample/layout/Verify.java deleted file mode 100644 index 496eea67288..00000000000 --- a/spring-boot-samples/spring-boot-sample-custom-layout/src/test/java/sample/layout/Verify.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2012-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 sample.layout; - -import java.io.File; -import java.util.Enumeration; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -public final class Verify { - - private Verify() { - } - - public static void verify(File file, String entry) throws Exception { - ZipFile zipFile = new ZipFile(file); - try { - Enumeration entries = zipFile.entries(); - while (entries.hasMoreElements()) { - if (entries.nextElement().getName().equals(entry)) { - return; - } - } - throw new AssertionError("No entry " + entry); - } - finally { - zipFile.close(); - } - } - -} diff --git a/spring-boot-samples/spring-boot-sample-logback/build.gradle b/spring-boot-samples/spring-boot-sample-logback/build.gradle index 19ca3a9c7b3..5033fc98186 100644 --- a/spring-boot-samples/spring-boot-sample-logback/build.gradle +++ b/spring-boot-samples/spring-boot-sample-logback/build.gradle @@ -19,15 +19,10 @@ apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'idea' apply plugin: 'org.springframework.boot' +apply plugin: 'io.spring.dependency-management' -jar { - baseName = 'spring-boot-sample-simple' - version = '0.0.0' -} - -bootRun { - systemProperties = System.properties -} +version = springBootVersion +group = 'org.springframework.boot' repositories { // NOTE: You should declare only repositories that you need here @@ -41,8 +36,4 @@ repositories { dependencies { compile("org.springframework.boot:spring-boot-starter") testCompile("org.springframework.boot:spring-boot-starter-test") -} - -task wrapper(type: Wrapper) { - gradleVersion = '1.6' -} +} \ No newline at end of file diff --git a/spring-boot-samples/spring-boot-sample-profile/build.gradle b/spring-boot-samples/spring-boot-sample-profile/build.gradle index 59e1f0d23a9..5033fc98186 100644 --- a/spring-boot-samples/spring-boot-sample-profile/build.gradle +++ b/spring-boot-samples/spring-boot-sample-profile/build.gradle @@ -19,23 +19,10 @@ apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'idea' apply plugin: 'org.springframework.boot' +apply plugin: 'io.spring.dependency-management' -jar { - baseName = 'spring-boot-sample-profile' - version = '0.0.0' - excludes = ['**/application.yml'] -} - -task('execJar', type:Jar, dependsOn: 'jar') { - baseName = 'spring-boot-sample-profile' - version = '0.0.0' - classifier = 'exec' - from sourceSets.main.output -} - -bootRepackage { - withJarTask = tasks['execJar'] -} +version = springBootVersion +group = 'org.springframework.boot' repositories { // NOTE: You should declare only repositories that you need here @@ -49,8 +36,4 @@ repositories { dependencies { compile("org.springframework.boot:spring-boot-starter") testCompile("org.springframework.boot:spring-boot-starter-test") -} - -task wrapper(type: Wrapper) { - gradleVersion = '1.6' -} +} \ No newline at end of file diff --git a/spring-boot-samples/spring-boot-sample-profile/pom.xml b/spring-boot-samples/spring-boot-sample-profile/pom.xml index 24df492afc7..358102090df 100644 --- a/spring-boot-samples/spring-boot-sample-profile/pom.xml +++ b/spring-boot-samples/spring-boot-sample-profile/pom.xml @@ -37,23 +37,6 @@ org.springframework.boot spring-boot-maven-plugin - - maven-jar-plugin - - - lib - - jar - - - lib - - application.yml - - - - - diff --git a/spring-boot-samples/spring-boot-sample-simple/build.gradle b/spring-boot-samples/spring-boot-sample-simple/build.gradle index 19ca3a9c7b3..36820a55ecb 100644 --- a/spring-boot-samples/spring-boot-sample-simple/build.gradle +++ b/spring-boot-samples/spring-boot-sample-simple/build.gradle @@ -19,15 +19,10 @@ apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'idea' apply plugin: 'org.springframework.boot' +apply plugin: 'io.spring.dependency-management' -jar { - baseName = 'spring-boot-sample-simple' - version = '0.0.0' -} - -bootRun { - systemProperties = System.properties -} +version = springBootVersion +group = 'org.springframework.boot' repositories { // NOTE: You should declare only repositories that you need here @@ -40,9 +35,6 @@ repositories { dependencies { compile("org.springframework.boot:spring-boot-starter") + compile("org.hibernate:hibernate-validator") testCompile("org.springframework.boot:spring-boot-starter-test") } - -task wrapper(type: Wrapper) { - gradleVersion = '1.6' -} diff --git a/spring-boot-samples/spring-boot-sample-web-static/build.gradle b/spring-boot-samples/spring-boot-sample-web-static/build.gradle index fed57e1af9e..1f777a1f6d5 100644 --- a/spring-boot-samples/spring-boot-sample-web-static/build.gradle +++ b/spring-boot-samples/spring-boot-sample-web-static/build.gradle @@ -15,17 +15,12 @@ buildscript { } } - -apply plugin: 'java' -apply plugin: 'eclipse-wtp' -apply plugin: 'idea' apply plugin: 'org.springframework.boot' +apply plugin: 'io.spring.dependency-management' apply plugin: 'war' -war { - baseName = 'spring-boot-sample-web-static' - version = '0.0.0' -} +version = springBootVersion +group = 'org.springframework.boot' repositories { // NOTE: You should declare only repositories that you need here @@ -36,10 +31,6 @@ repositories { maven { url "http://repo.spring.io/snapshot" } } -configurations { - providedRuntime -} - dependencies { compile("org.springframework.boot:spring-boot-starter-web") compile("org.webjars:bootstrap:3.0.3") @@ -47,5 +38,3 @@ dependencies { providedRuntime("org.springframework.boot:spring-boot-starter-tomcat") testCompile("org.springframework.boot:spring-boot-starter-test") } - -task wrapper(type: Wrapper) { gradleVersion = '1.6' } diff --git a/spring-boot-samples/spring-boot-sample-web-ui/build.gradle b/spring-boot-samples/spring-boot-sample-web-ui/build.gradle index e57c30fbf13..67602012a14 100644 --- a/spring-boot-samples/spring-boot-sample-web-ui/build.gradle +++ b/spring-boot-samples/spring-boot-sample-web-ui/build.gradle @@ -17,20 +17,11 @@ buildscript { } } - apply plugin: 'java' -apply plugin: 'eclipse' -apply plugin: 'idea' apply plugin: 'org.springframework.boot' +apply plugin: 'io.spring.dependency-management' -springBoot { - classifier = 'exec' -} - -jar { - baseName = 'spring-boot-sample-web-ui' - version = '0.0.0' -} +version = springBootVersion repositories { // NOTE: You should declare only repositories that you need here @@ -46,5 +37,3 @@ dependencies { compile("org.hibernate:hibernate-validator") testCompile("org.springframework.boot:spring-boot-starter-test") } - -task wrapper(type: Wrapper) { gradleVersion = '1.6' } From 01166381a0eb8de20ef2213728011856d16cf729 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 14 Mar 2017 20:51:23 +0000 Subject: [PATCH 30/30] Provide separate documentation (API and reference) for Gradle plugin --- spring-boot-docs/pom.xml | 9 + .../src/main/asciidoc/build-tool-plugins.adoc | 515 +-------------- spring-boot-docs/src/main/asciidoc/howto.adoc | 105 +-- spring-boot-docs/src/main/asciidoc/index.adoc | 6 +- .../src/main/asciidoc/using-spring-boot.adoc | 74 +-- .../spring-boot-gradle-plugin/build.gradle | 13 +- .../spring-boot-gradle-plugin/pom.xml | 134 ++++ .../src/main/asciidoc/getting-started.adoc | 43 ++ .../src/main/asciidoc/index.adoc | 48 ++ .../asciidoc/integrating-with-actuator.adoc | 55 ++ .../main/asciidoc/managing-dependencies.adoc | 46 ++ .../src/main/asciidoc/packaging.adoc | 167 +++++ .../src/main/asciidoc/publishing.adoc | 43 ++ .../src/main/asciidoc/reacting.adoc | 78 +++ .../src/main/asciidoc/running.adoc | 49 ++ .../apply-plugin-milestone.gradle | 11 + .../apply-plugin-release.gradle | 3 + .../apply-plugin-snapshot.gradle | 11 + .../getting-started/typical-plugins.gradle | 17 + .../build-info-additional.gradle | 21 + .../build-info-basic.gradle | 14 + .../build-info-custom-values.gradle | 21 + .../custom-version.gradle | 32 + .../managing-dependencies/dependencies.gradle | 19 + .../application-plugin-main-class.gradle | 13 + .../boot-jar-custom-launch-script.gradle | 21 + .../boot-jar-include-launch-script.gradle | 20 + .../boot-jar-launch-script-properties.gradle | 21 + .../packaging/boot-jar-main-class.gradle | 14 + .../boot-jar-manifest-main-class.gradle | 16 + .../packaging/boot-jar-requires-unpack.gradle | 26 + .../boot-war-include-devtools.gradle | 19 + .../boot-war-properties-launcher.gradle | 20 + .../packaging/war-container-dependency.gradle | 19 + .../gradle/publishing/maven-publish.gradle | 31 + .../src/main/gradle/publishing/maven.gradle | 25 + .../application-plugin-main-class-name.gradle | 19 + .../main/gradle/running/boot-run-main.gradle | 20 + .../running/boot-run-source-resources.gradle | 20 + .../src/main/javadoc/spring-javadoc.css | 599 ++++++++++++++++++ .../GettingStartedDocumentationTests.java | 47 ++ ...gratingWithActuatorDocumentationTests.java | 90 +++ ...anagingDependenciesDocumentationTests.java | 50 ++ .../docs/PackagingDocumentationTests.java | 176 +++++ .../docs/PublishingDocumentationTests.java | 54 ++ .../docs/RunningDocumentationTests.java | 60 ++ .../boot/gradle/testkit/GradleBuild.java | 11 +- 47 files changed, 2247 insertions(+), 678 deletions(-) create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/getting-started.adoc create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/index.adoc create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/integrating-with-actuator.adoc create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/managing-dependencies.adoc create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/packaging.adoc create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/publishing.adoc create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/reacting.adoc create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/running.adoc create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/getting-started/apply-plugin-milestone.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/getting-started/apply-plugin-release.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/getting-started/apply-plugin-snapshot.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/getting-started/typical-plugins.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/integrating-with-actuator/build-info-additional.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/integrating-with-actuator/build-info-basic.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/integrating-with-actuator/build-info-custom-values.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/managing-dependencies/custom-version.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/managing-dependencies/dependencies.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/application-plugin-main-class.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-jar-custom-launch-script.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-jar-include-launch-script.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-jar-launch-script-properties.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-jar-main-class.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-jar-manifest-main-class.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-jar-requires-unpack.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-war-include-devtools.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-war-properties-launcher.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/war-container-dependency.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/publishing/maven-publish.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/publishing/maven.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/running/application-plugin-main-class-name.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/running/boot-run-main.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/running/boot-run-source-resources.gradle create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/javadoc/spring-javadoc.css create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/GettingStartedDocumentationTests.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/IntegratingWithActuatorDocumentationTests.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/ManagingDependenciesDocumentationTests.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/PackagingDocumentationTests.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/PublishingDocumentationTests.java create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/RunningDocumentationTests.java diff --git a/spring-boot-docs/pom.xml b/spring-boot-docs/pom.xml index 7c96c172e99..33788d4b041 100644 --- a/spring-boot-docs/pom.xml +++ b/spring-boot-docs/pom.xml @@ -853,6 +853,15 @@ META-INF/** + + org.springframework.boot + spring-boot-gradle-plugin + ${project.version} + docs + zip + ${project.build.directory}/contents/gradle-plugin + META-INF/** + diff --git a/spring-boot-docs/src/main/asciidoc/build-tool-plugins.adoc b/spring-boot-docs/src/main/asciidoc/build-tool-plugins.adoc index 1f914771ba6..d0de570135d 100644 --- a/spring-boot-docs/src/main/asciidoc/build-tool-plugins.adoc +++ b/spring-boot-docs/src/main/asciidoc/build-tool-plugins.adoc @@ -164,516 +164,11 @@ Advanced configuration options and examples are available in the [[build-tool-plugins-gradle-plugin]] == Spring Boot Gradle plugin -The Spring Boot Gradle Plugin provides Spring Boot support in Gradle, allowing you to -package executable jar or war archives, run Spring Boot applications and use the -dependency management provided by `spring-boot-dependencies`. - - - -[[build-tool-plugins-including-the-gradle-plugin]] -=== Including the plugin -ifeval::["{spring-boot-repo}" == "release"] -To use the Spring Boot Gradle Plugin configure it using the `plugins` block: -[source,groovy,indent=0,subs="verbatim,attributes"] ----- - plugins { - id 'org.springframework.boot' version '{spring-boot-version}' - } ----- -endif::[] -ifeval::["{spring-boot-repo}" != "release"] -To use the Spring Boot Gradle Plugin simply include a `buildscript` dependency and apply -the `spring-boot` plugin: - -[source,groovy,indent=0,subs="verbatim,attributes"] ----- - buildscript { - repositories { - maven { url 'http://repo.spring.io/snapshot' } - maven { url 'http://repo.spring.io/milestone' } - } - dependencies { - classpath 'org.springframework.boot:spring-boot-gradle-plugin:{spring-boot-version}'' - } - } - apply plugin: 'org.springframework.boot' ----- -endif::[] - - - -[[build-tool-plugins-gradle-dependency-management]] -=== Gradle dependency management -The `spring-boot` plugin automatically applies the -{dependency-management-plugin}/[Dependency Management Plugin] and configures it to import -the `spring-boot-starter-parent` bom. This provides a similar dependency management -experience to the one that is enjoyed by Maven users. For example, it allows you to omit -version numbers when declaring dependencies that are managed in the bom. To make use of -this functionality, simply declare dependencies in the usual way, but leave the version -number empty: - -[source,groovy,indent=0,subs="verbatim,attributes"] ----- - dependencies { - compile("org.springframework.boot:spring-boot-starter-web") - compile("org.thymeleaf:thymeleaf-spring5") - compile("nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect") - } ----- - -NOTE: The version of the `spring-boot` gradle plugin that you declare determines the -version of the `spring-boot-starter-parent` bom that is imported (this ensures that builds -are always repeatable). You should always set the version of the `spring-boot` gradle -plugin to the actual Spring Boot version that you wish to use. Details of the versions -that are provided can be found in the <>. - -To learn more about the capabilities of the Dependency Management Plugin, please refer to -its {dependency-management-plugin-documentation}[documentation]. - - - -[[build-tool-plugins-gradle-packaging]] -=== Packaging executable jar and war files -Once the `spring-boot` plugin has been applied to your project it will automatically -attempt to rewrite archives to make them executable using the -<>. You -should configure your project to build a jar or war (as appropriate) in the usual way. - -The main class that you want to launch can either be specified using a configuration -option, or by adding a `Main-Class` attribute to the manifest. If you don't specify a -main class the plugin will search for a class with a -`public static void main(String[] args)` method. - -TIP: Check <> for a full list of -configuration options. - -To build and run a project artifact, you can type the following: - -[indent=0] ----- - $ gradle build - $ java -jar build/libs/mymodule-0.0.1-SNAPSHOT.jar ----- - -To build a war file that is both executable and deployable into an external container, -you need to mark the embedded container dependencies as belonging to the war plugin's -`providedRuntime` configuration, e.g.: - -[source,groovy,indent=0,subs="verbatim,attributes"] ----- - ... - apply plugin: 'war' - - war { - baseName = 'myapp' - version = '0.5.0' - } - - repositories { - jcenter() - maven { url "http://repo.spring.io/libs-snapshot" } - } - - dependencies { - compile("org.springframework.boot:spring-boot-starter-web") - providedRuntime("org.springframework.boot:spring-boot-starter-tomcat") - ... - } ----- - -TIP: See the "`<>`" section for more details on -how to create a deployable war file. - - - -[[build-tool-plugins-gradle-running-applications]] -=== Running a project in-place -To run a project in place without building a jar first you can use the "`bootRun`" task: - -[indent=0] ----- - $ gradle bootRun ----- - -If <> has been added to your project -it will automatically monitor your application for changes. Alternatively, you can also -run the application so that your static classpath resources (i.e. in `src/main/resources` -by default) are reloadable in the live application, which can be helpful at development -time. - -[source,groovy,indent=0,subs="verbatim,attributes"] ----- - bootRun { - addResources = true - } ----- - -Making static classpath resources reloadable means that `bootRun` does not use the output -of the `processResources` task, i.e., when invoked using `bootRun`, your application will -use the resources in their unprocessed form. - - - -[[build-tool-plugins-gradle-global-configuration]] -=== Spring Boot plugin configuration -The gradle plugin automatically extends your build script DSL with a `springBoot` element -for global configuration of the Boot plugin. Set the appropriate properties as you would -with any other Gradle extension (see below for a list of configuration options): - -[source,groovy,indent=0,subs="verbatim,attributes"] ----- - springBoot { - backupSource = false - } ----- - - - -[[build-tool-plugins-gradle-repackage-configuration]] -=== Repackage configuration -The plugin adds a `bootRepackage` task which you can also configure directly, e.g.: - -[source,groovy,indent=0,subs="verbatim,attributes"] ----- - bootRepackage { - mainClass = 'demo.Application' - } ----- - -The following configuration options are available: - -[cols="2,4"] -|=== -|Name |Description - -|`enabled` -|Boolean flag to switch the repackager off (sometimes useful if you -want the other Boot features but not this one) - -|`mainClass` -|The main class that should be run. If not specified, and you have applied the application - plugin, the `mainClassName` project property will be used. If the application plugin has - not been applied or no `mainClassName` has been specified, the archive will be searched - for a suitable class. "Suitable" means a unique class with a well-formed `main()` method - (if more than one is found the build will fail). If you have applied the application - plugin, the main class can also be specified via its "run" task (`main` property) and/or - its "startScripts" task (`mainClassName` property) as an alternative to using the - "springBoot" configuration. - -|`classifier` -|A file name segment (before the extension) to add to the archive, so that the original is - preserved in its original location. Defaults to `null` in which case the archive is - repackaged in place. The default is convenient for many purposes, but if you want to use - the original jar as a dependency in another project you must use a classifier to define - the executable archive. - -|`withJarTask` -|The name or value of the `Jar` task (defaults to all tasks of type `Jar`) which is used - to locate the archive to repackage. - -|`customConfiguration` -|The name of the custom configuration which is used to populate the nested lib directory - (without specifying this you get all compile and runtime dependencies). - -|`executable` -|Boolean flag to indicate if jar files are fully executable on Unix like operating - systems. Defaults to `false`. - -|`embeddedLaunchScript` -|The embedded launch script to prepend to the front of the jar if it is fully executable. - If not specified the 'Spring Boot' default script will be used. - -|`embeddedLaunchScriptProperties` -|Additional properties that to be expanded in the launch script. The default script - supports a `mode` property which can contain the values `auto`, `service` or `run`. - -|`excludeDevtools` -|Boolean flag to indicate if the devtools jar should be excluded from the repackaged -archives. Defaults to `true`. -|=== - - - -[[build-tool-plugins-gradle-repackage-custom-configuration]] -=== Repackage with custom Gradle configuration -Sometimes it may be more appropriate to not package default dependencies resolved from -`compile`, `runtime` and `provided` scopes. If the created executable jar file -is intended to be run as it is, you need to have all dependencies nested inside it; -however, if the plan is to explode a jar file and run the main class manually, you may already -have some of the libraries available via `CLASSPATH`. This is a situation where -you can repackage your jar with a different set of dependencies. - -Using a custom -configuration will automatically disable dependency resolving from -`compile`, `runtime` and `provided` scopes. Custom configuration can be either -defined globally (inside the `springBoot` section) or per task. - -[source,groovy,indent=0,subs="verbatim,attributes"] ----- - task clientJar(type: Jar) { - appendix = 'client' - from sourceSets.main.output - exclude('**/*Something*') - } - - task clientBoot(type: BootRepackage, dependsOn: clientJar) { - withJarTask = clientJar - customConfiguration = "mycustomconfiguration" - } ----- - -In above example, we created a new `clientJar` Jar task to package a customized -file set from your compiled sources. Then we created a new `clientBoot` -BootRepackage task and instructed it to work with only `clientJar` task and -`mycustomconfiguration`. - -[source,groovy,indent=0,subs="verbatim,attributes"] ----- - configurations { - mycustomconfiguration.exclude group: 'log4j' - } - - dependencies { - mycustomconfiguration configurations.runtime - } ----- - -The configuration that we are referring to in `BootRepackage` is a normal -{gradle-dsl}/org.gradle.api.artifacts.Configuration.html[Gradle -configuration]. In the above example we created a new configuration named -`mycustomconfiguration` instructing it to derive from a `runtime` and exclude the `log4j` -group. If the `clientBoot` task is executed, the repackaged boot jar will have all -dependencies from `runtime` but no `log4j` jars. - - - -[[build-tool-plugins-gradle-configuration-options]] -==== Configuration options -The following configuration options are available: - -[cols="2,4"] -|=== -|Name |Description - -|`mainClass` -|The main class that should be run by the executable archive. - -|`providedConfiguration` -|The name of the provided configuration (defaults to `providedRuntime`). - -|`backupSource` -|If the original source archive should be backed-up before being repackaged (defaults - to `true`). - -|`customConfiguration` -|The name of the custom configuration. - -|`layout` -|The type of archive, corresponding to how the dependencies are laid out inside - (defaults to a guess based on the archive type). See - <>. - -|`layoutFactory` -|A layout factory that can be used if a custom layout is required. Alternative layouts -can be provided by 3rd parties. Layout factories are only used when `layout` is not -specified. - -|`requiresUnpack` -|A list of dependencies (in the form "`groupId:artifactId`" that must be unpacked from - fat jars in order to run. Items are still packaged into the fat jar, but they will be - automatically unpacked when it runs. -|=== - - - -[[build-tool-plugins-gradle-configuration-layouts]] -==== Available layouts - -The `layout` attribute configures the format of the archive and whether the bootstrap -loader should be included or not. The following layouts are available: - -[cols="2,4,1"] -|=== -|Name |Description |Executable - -|`JAR` -|Regular executable - <>. -|Yes - -|`WAR` -|Executable - <>. - `provided` dependencies are placed in `WEB-INF/lib-provided` to avoid any clash when - the `war` is deployed in a servlet container. -|Yes - -|`ZIP` (alias to `DIR`) -|Similar to `JAR` layout, using - <>. -| Yes - -|`MODULE` -|Bundle dependencies (excluding those with `provided` scope) and project resources. -|No - -|`NONE` -|Bundle all dependencies and project resources. -|No -|=== - - - -[[build-tool-plugins-gradle-configuration-custom-repackager]] -==== Using a custom layout -If you have custom requirements for how to arrange the dependencies and loader classes -inside the repackaged jar, you can use a custom layout. Any library which defines one -or more `LayoutFactory` implementations can be added to the build script dependencies -and then the layout factory becomes available in the `springBoot` configuration. -For example: - -[source,groovy,indent=0,subs="verbatim,attributes"] ----- -buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath("org.springframework.boot:spring-boot-gradle-plugin:{spring-boot-version}") - classpath("com.example:custom-layout:1.0.0") - } -} - -springBoot { - layoutFactory = new com.example.CustomLayoutFactory() -} ----- - -NOTE: If there is only one custom `LayoutFactory` on the build classpath and it is -listed in `META-INF/spring.factories` then it is unnecessary to explicitly set it in the -`springBoot` configuration. Layout factories are only used when no explicit `layout` is -specified. - - - -[[build-tool-plugins-understanding-the-gradle-plugin]] -=== Understanding how the Gradle plugin works -When `spring-boot` is applied to your Gradle project a default task named `bootRepackage` -is created automatically. The `bootRepackage` task depends on Gradle `assemble` task, and -when executed, it tries to find all jar artifacts whose qualifier is empty (i.e. tests and -sources jars are automatically skipped). - -Due to the fact that `bootRepackage` finds 'all' created jar artifacts, the order of -Gradle task execution is important. Most projects only create a single jar file, so -usually this is not an issue; however, if you are planning to create a more complex -project setup, with custom `Jar` and `BootRepackage` tasks, there are few tweaks to -consider. - -If you are 'just' creating custom jar files from your project you can simply disable -default `jar` and `bootRepackage` tasks: - -[source,groovy,indent=0,subs="verbatim,attributes"] ----- - jar.enabled = false - bootRepackage.enabled = false ----- - -Another option is to instruct the default `bootRepackage` task to only work with a -default `jar` task. - -[source,groovy,indent=0,subs="verbatim,attributes"] ----- - bootRepackage.withJarTask = jar ----- - -If you have a default project setup where the main jar file is created and repackaged, -'and' you still want to create additional custom jars, you can combine your custom -repackage tasks together and use `dependsOn` so that the `bootJars` task will run after -the default `bootRepackage` task is executed: - -[source,groovy,indent=0,subs="verbatim,attributes"] ----- - task bootJars - bootJars.dependsOn = [clientBoot1,clientBoot2,clientBoot3] - build.dependsOn(bootJars) ----- - -All the above tweaks are usually used to avoid situations where an already created boot -jar is repackaged again. Repackaging an existing boot jar will not break anything, but -you may find that it includes unnecessary dependencies. - - - -[[build-tool-plugins-gradle-publishing-artifacts-to-a-maven-repository]] -=== Publishing artifacts to a Maven repository using Gradle -If you are <> and you want to publish artifacts to a Maven repository -you will need to configure the Maven publication with details of Spring Boot's -dependency management. This can be achieved by configuring it to publish poms that -inherit from `spring-boot-starter-parent` or that import dependency management from -`spring-boot-dependencies`. The exact details of this configuration depend on how you're -using Gradle and how you're trying to publish the artifacts. - - - -[[build-tool-plugins-gradle-publishing-artifacts-to-a-maven-repository-inherit]] -==== Configuring Gradle to produce a pom that inherits dependency management -The following is an example of configuring Gradle to generate a pom that inherits -from `spring-boot-starter-parent`. Please refer to the -{gradle-user-guide}/userguide.html[Gradle User Guide] for further information. - -[source,groovy,indent=0,subs="verbatim,attributes"] ----- - uploadArchives { - repositories { - mavenDeployer { - pom { - project { - parent { - groupId "org.springframework.boot" - artifactId "spring-boot-starter-parent" - version "{spring-boot-version}" - } - } - } - } - } - } ----- - - - -[[build-tool-plugins-gradle-publishing-artifacts-to-a-maven-repository-import]] -==== Configuring Gradle to produce a pom that imports dependency management -The following is an example of configuring Gradle to generate a pom that imports -the dependency management provided by `spring-boot-dependencies`. Please refer to the -http://www.gradle.org/docs/current/userguide/userguide.html[Gradle User Guide] for -further information. - -[source,groovy,indent=0,subs="verbatim,attributes"] ----- - uploadArchives { - repositories { - mavenDeployer { - pom { - project { - dependencyManagement { - dependencies { - dependency { - groupId "org.springframework.boot" - artifactId "spring-boot-dependencies" - version "{spring-boot-version}" - type "pom" - scope "import" - } - } - } - } - } - } - } - } ----- +The {spring-boot-gradle-plugin}[Spring Boot Gradle Plugin] provides Spring Boot support +in Gradle, allowing you to package executable jar or war archives, run Spring Boot +applications and use the dependency management provided by `spring-boot-dependencies`. +It requires Gradle 3.4 or later. Please refer to the {spring-boot-gradle-plugin}[plugin's +documentation] to learn more. diff --git a/spring-boot-docs/src/main/asciidoc/howto.adoc b/spring-boot-docs/src/main/asciidoc/howto.adoc index e4fea6f5903..368919a2138 100644 --- a/spring-boot-docs/src/main/asciidoc/howto.adoc +++ b/spring-boot-docs/src/main/asciidoc/howto.adoc @@ -827,12 +827,12 @@ Example in Gradle: [source,groovy,indent=0,subs="verbatim,quotes,attributes"] ---- configurations { - compile.exclude module: "spring-boot-starter-tomcat" + compile.exclude module: 'spring-boot-starter-tomcat' } dependencies { - compile("org.springframework.boot:spring-boot-starter-web:{spring-boot-version}") - compile("org.springframework.boot:spring-boot-starter-jetty:{spring-boot-version}") + compile 'org.springframework.boot:spring-boot-starter-web' + compile 'org.springframework.boot:spring-boot-starter-jetty' // ... } ---- @@ -881,12 +881,12 @@ Example in Gradle: [source,groovy,indent=0,subs="verbatim,quotes,attributes"] ---- configurations { - compile.exclude module: "spring-boot-starter-tomcat" + compile.exclude module: 'spring-boot-starter-tomcat' } dependencies { - compile("org.springframework.boot:spring-boot-starter-web:{spring-boot-version}") - compile("org.springframework.boot:spring-boot-starter-undertow:{spring-boot-version}") + compile 'org.springframework.boot:spring-boot-starter-web' + compile 'org.springframework.boot:spring-boot-starter-undertow' // ... } ---- @@ -2355,9 +2355,11 @@ To configure IntelliJ IDEA correctly you can use the `idea` Gradle plugin: [source,groovy,indent=0,subs="verbatim,attributes"] ---- buildscript { - repositories { jcenter() } + repositories { + jcenter() + } dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:{spring-boot-version}" + classpath 'org.springframework.boot:spring-boot-gradle-plugin:{spring-boot-version}' classpath 'org.springframework:springloaded:1.2.6.RELEASE' } } @@ -2424,7 +2426,7 @@ And to do the same with Gradle: [source,groovy,indent=0,subs="verbatim,attributes"] ---- - springBoot { + springBoot { buildInfo() } ---- @@ -2502,16 +2504,7 @@ the artifact yourself instead of overriding the property. WARNING: Each Spring Boot release is designed and tested against a specific set of third-party dependencies. Overriding versions may cause compatibility issues. -To override dependency versions in Gradle, you can specify a version as shown below: - -[source,groovy,indent=0] ----- - ext['slf4j.version'] = '1.7.5' ----- -For additional information, please refer to the -https://github.com/spring-gradle-plugins/dependency-management-plugin[Gradle Dependency -Management Plugin documentation]. [[howto-create-an-executable-jar-with-maven]] === Create an executable JAR with Maven @@ -2593,15 +2586,6 @@ To configure a classifier of `exec` in Maven, the following configuration can be ---- -And when using Gradle, the following configuration can be used: - -[source,groovy,indent=0,subs="verbatim,attributes"] ----- - bootRepackage { - classifier = 'exec' - } ----- - [[howto-extract-specific-libraries-when-an-executable-jar-runs]] @@ -2637,15 +2621,6 @@ you would add the following configuration: ---- -And to do that same with Gradle: - -[source,groovy,indent=0,subs="verbatim,attributes"] ----- - springBoot { - requiresUnpack = ['org.jruby:jruby-complete'] - } ----- - [[howto-create-a-nonexecutable-jar]] @@ -2689,29 +2664,6 @@ jar must be the main artifact and you can add a classified jar for the library: ---- -In Gradle you can create a new JAR archive with standard task DSL features, and then have -the `bootRepackage` task depend on that one using its `withJarTask` property: - -[source,groovy,indent=0,subs="verbatim,attributes"] ----- - jar { - baseName = 'spring-boot-sample-profile' - version = '0.0.0' - excludes = ['**/application.yml'] - } - - task('execJar', type:Jar, dependsOn: 'jar') { - baseName = 'spring-boot-sample-profile' - version = '0.0.0' - classifier = 'exec' - from sourceSets.main.output - } - - bootRepackage { - withJarTask = tasks['execJar'] - } ----- - [[howto-remote-debug-maven-run]] @@ -2723,34 +2675,6 @@ Check {spring-boot-maven-plugin-site}/examples/run-debug.html[this example] for -[[howto-remote-debug-gradle-run]] -=== Remote debug a Spring Boot application started with Gradle -To attach a remote debugger to a Spring Boot application started with Gradle you can use -the `jvmArgs` property of `bootRun` task or `--debug-jvm` command line option. - -`build.gradle`: - -[source,groovy,indent=0,subs="verbatim,attributes"] ----- - bootRun { - jvmArgs "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005" - } ----- - - -Command line: - -[indent=0] ----- - $ gradle bootRun --debug-jvm ----- - - -Check {gradle-userguide}/application_plugin.html[Gradle Application Plugin] for more -details. - - - [[howto-build-an-executable-archive-with-ant]] === Build an executable archive from Ant without using spring-boot-antlib To build with Ant you need to grab dependencies, compile and then create a jar or war @@ -2894,10 +2818,9 @@ And if you're using Gradle: } ---- -NOTE: If you are using a version of Gradle that supports compile only dependencies (2.12 -or later), you should continue to use `providedRuntime`. Among other limitations, -`compileOnly` dependencies are not on the test classpath so any web-based integration -tests will fail. +NOTE: `providedRuntime` is prefered to Gradle's `compileOnly` configuration as, among other +limitations, `compileOnly` dependencies are not on the test classpath so any web-based +integration tests will fail. If you're using the <>, marking the embedded servlet container dependency as provided will produce an executable war diff --git a/spring-boot-docs/src/main/asciidoc/index.adoc b/spring-boot-docs/src/main/asciidoc/index.adoc index de0438ccfb3..d79d7e60b59 100644 --- a/spring-boot-docs/src/main/asciidoc/index.adoc +++ b/spring-boot-docs/src/main/asciidoc/index.adoc @@ -34,7 +34,8 @@ Phillip Webb; Dave Syer; Josh Long; Stéphane Nicoll; Rob Winch; Andy Wilkinson; :dc-spring-boot-test-autoconfigure: {dc-root}/org/springframework/boot/test/autoconfigure :dependency-management-plugin: https://github.com/spring-gradle-plugins/dependency-management-plugin :dependency-management-plugin-documentation: {dependency-management-plugin}/blob/master/README.md -:spring-boot-maven-plugin-site: http://docs.spring.io/spring-boot/docs/{spring-boot-docs-version}/maven-plugin +:spring-boot-maven-plugin-site: http://docs.spring.io/spring-boot/docs/{spring-boot-docs-version}/maven-plugin/ +:spring-boot-gradle-plugin: http://docs.spring.io/spring-boot/docs/{spring-boot-docs-version}/gradle-plugin/ :spring-reference: http://docs.spring.io/spring/docs/{spring-docs-version}/spring-framework-reference/htmlsingle :spring-security-reference: http://docs.spring.io/spring-security/site/docs/{spring-security-docs-version}/reference/htmlsingle :spring-security-oauth2-reference: http://projects.spring.io/spring-security-oauth/docs/oauth2.html @@ -50,8 +51,7 @@ Phillip Webb; Dave Syer; Josh Long; Stéphane Nicoll; Rob Winch; Andy Wilkinson; :propdeps-plugin: https://github.com/spring-projects/gradle-plugins/tree/master/propdeps-plugin :ant-manual: http://ant.apache.org/manual :code-examples: ../java/org/springframework/boot -:gradle-user-guide: https://docs.gradle.org/2.14.1/userguide -:gradle-dsl: https://docs.gradle.org/2.14.1/dsl +:gradle-user-guide: https://docs.gradle.org/3.4.1/userguide // ====================================================================================== include::documentation-overview.adoc[] diff --git a/spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc b/spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc index 8b8ea14149a..7ac1ddb5578 100644 --- a/spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc +++ b/spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc @@ -41,8 +41,8 @@ feel that's necessary. The curated list contains all the spring modules that you can use with Spring Boot as well as a refined list of third party libraries. The list is available as a standard <> -and additional dedicated support for <> and -<> are available as well. +that can be used with both <> and +{spring-boot-gradle-plugin}[Gradle]. WARNING: Each release of Spring Boot is associated with a base version of the Spring Framework so we **highly** recommend you to not specify its version on your own. @@ -194,70 +194,8 @@ the parent. [[using-boot-gradle]] === Gradle -Gradle users can directly import '`starters`' in their `dependencies` section. Unlike -Maven, there is no "`super parent`" to import to share some configuration. - -[source,groovy,indent=0,subs="attributes"] ----- - repositories { -ifeval::["{spring-boot-repo}" != "release"] - maven { url "http://repo.spring.io/snapshot" } - maven { url "http://repo.spring.io/milestone" } -endif::[] -ifeval::["{spring-boot-repo}" == "release"] - jcenter() -endif::[] - } - - dependencies { - compile("org.springframework.boot:spring-boot-starter-web:{spring-boot-version}") - } ----- - -The <> is also available and provides tasks to create executable -jars and run projects from source. It also provides -<> that, among -other capabilities, allows you to omit the version number for any dependencies that are -managed by Spring Boot: - -[source,groovy,indent=0,subs="attributes"] ----- -ifeval::["{spring-boot-repo}" == "release"] - plugins { - id 'org.springframework.boot' version '{spring-boot-version}' - id 'java' - } -endif::[] -ifeval::["{spring-boot-repo}" != "release"] - buildscript { - repositories { - jcenter() - maven { url 'http://repo.spring.io/snapshot' } - maven { url 'http://repo.spring.io/milestone' } - } - dependencies { - classpath 'org.springframework.boot:spring-boot-gradle-plugin:{spring-boot-version}' - } - } - - apply plugin: 'java' - apply plugin: 'org.springframework.boot' -endif::[] - - repositories { - jcenter() -ifeval::["{spring-boot-repo}" != "release"] - maven { url 'http://repo.spring.io/snapshot' } - maven { url 'http://repo.spring.io/milestone' } -endif::[] - } - - dependencies { - compile("org.springframework.boot:spring-boot-starter-web") - testCompile("org.springframework.boot:spring-boot-starter-test") - } ----- +To learn about using Spring Boot with Gradle, please refer to the +{spring-boot-gradle-plugin}[documentation for Spring Boot's Gradle plugin]. @@ -704,8 +642,8 @@ You might also want to use the useful operating system environment variable: [[using-boot-running-with-the-gradle-plugin]] === Using the Gradle plugin The Spring Boot Gradle plugin also includes a `bootRun` task which can be used to run -your application in an exploded form. The `bootRun` task is added whenever you import -the `spring-boot-gradle-plugin`: +your application in an exploded form. The `bootRun` task is added whenever you apply the +the `org.springframework.boot` and `java` plugins: [indent=0,subs="attributes"] ---- diff --git a/spring-boot-tools/spring-boot-gradle-plugin/build.gradle b/spring-boot-tools/spring-boot-gradle-plugin/build.gradle index 01b42fef767..cc3fcc49751 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/build.gradle +++ b/spring-boot-tools/spring-boot-gradle-plugin/build.gradle @@ -46,7 +46,6 @@ jar { } } - eclipseJdt { inputFile = rootProject.file('../../eclipse/org.eclipse.jdt.core.prefs') doLast { @@ -56,6 +55,18 @@ eclipseJdt { } } +javadoc { + options { + author() + stylesheetFile = file('src/main/javadoc/spring-javadoc.css') + links = [ + 'http://docs.oracle.com/javase/8/docs/api/', + 'https://docs.gradle.org/current/javadoc/' + ] + } + title = "${project.description} $version API" +} + task sourcesJar(type: Jar) { classifier = 'sources' from sourceSets.main.allSource diff --git a/spring-boot-tools/spring-boot-gradle-plugin/pom.xml b/spring-boot-tools/spring-boot-gradle-plugin/pom.xml index e289a5b3803..a9e432ece70 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/pom.xml +++ b/spring-boot-tools/spring-boot-gradle-plugin/pom.xml @@ -58,6 +58,7 @@ clean build -Pversion=${project.version} + -Pdescription=${project.description} @@ -112,5 +113,138 @@ gradlew.bat + + full + + + + org.apache.maven.plugins + maven-antrun-plugin + + + ant-contrib + ant-contrib + 1.0b3 + + + ant + ant + + + + + org.apache.ant + ant-nodeps + 1.8.1 + + + org.tigris.antelope + antelopetasks + 3.2.10 + + + + + set-up-maven-properties + prepare-package + + run + + + true + + + + + + + + + + + + + + + package-docs-zip + package + + run + + + + + + + + + + + + + + org.asciidoctor + asciidoctor-maven-plugin + 1.5.3 + + + generate-html-documentation + prepare-package + + process-asciidoc + + + html + + + + generate-pdf-documentation + prepare-package + + process-asciidoc + + + pdf + + + + + + ${version-type} + ${project.version} + + + + + org.asciidoctor + asciidoctorj-pdf + 1.5.0-alpha.11 + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + attach-zip + + attach-artifact + + + + + ${project.build.directory}/${project.artifactId}-${project.version}-docs.zip + zip + docs + + + + + + + + + diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/getting-started.adoc b/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/getting-started.adoc new file mode 100644 index 00000000000..0ec83436f45 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/getting-started.adoc @@ -0,0 +1,43 @@ +[[getting-started]] +== Getting started + +To get started with the plugin it needs to be applied to your project. + +ifeval::["{version-type}" == "RELEASE"] +The plugin is https://plugins.gradle.org/plugin/org.springframework.boot[published to +Gradle's plugin portal] and can be applied using the `plugins` block: +[source,groovy,indent=0,subs="verbatim,attributes"] +---- +include::../gradle/getting-started/apply-plugin-release.gradle[] +---- +endif::[] +ifeval::["{version-type}" == "MILESTONE"] +[source,groovy,indent=0,subs="verbatim,attributes"] +---- +include::../gradle/getting-started/apply-plugin-milestone.gradle[] +---- +endif::[] +ifeval::["{version-type}" == "SNAPSHOT"] +[source,groovy,indent=0,subs="verbatim,attributes"] +---- +include::../gradle/getting-started/apply-plugin-snapshot.gradle[] +---- +endif::[] + +Applied in isolation the plugin makes few changes to a project. Instead, the plugin +detects when certain other plugins are applied and reacts accordingly. For example, when +the `java` plugin is applied a task for building an executable jar is automatically +configured. + +A typical Spring Boot project will apply the {groovy-plugin}[`groovy`], +{java-plugin}java_plugin.html[`java`], or {kotlin-plugin}[`org.jetbrains.kotlin.jvm`] +plugin and the {dependency-management-plugin}[`io.spring.dependency-management`] plugin as +a minimum. For example: + +[source,groovy,indent=0,subs="verbatim,attributes"] +---- +include::../gradle/getting-started/typical-plugins.gradle[tags=apply] +---- + +To learn more about how the Spring Boot plugin behaves when other plugins are applied +please see the section on <>. \ No newline at end of file diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/index.adoc b/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/index.adoc new file mode 100644 index 00000000000..17645f4e9f8 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/index.adoc @@ -0,0 +1,48 @@ += Spring Boot Gradle Plugin Reference Guide +Andy Wilkinson +:doctype: book +:toc: left +:toclevels: 4 +:source-highlighter: prettify +:numbered: +:icons: font +:hide-uri-scheme: +:dependency-management-plugin: https://github.com/spring-gradle-plugins/dependency-management-plugin +:dependency-management-plugin-documentation: {dependency-management-plugin}/blob/master/README.md +:gradle-userguide: http://www.gradle.org/docs/current/userguide +:gradle-dsl: https://docs.gradle.org/current/dsl +:application-plugin: {gradle-userguide}/application_plugin.html +:groovy-plugin: {gradle-userguide}/groovy_plugin.html +:java-plugin: {gradle-userguide}/java_plugin.html +:war-plugin: {gradle-userguide}/war_plugin.html +:maven-plugin: {gradle-userguide}/maven_plugin.html +:maven-publish-plugin: {gradle-userguide}/maven_publish_plugin.html +:software-component: {gradle-userguide}/software_model_extend.html +:kotlin-plugin: https://kotlinlang.org/docs/reference/using-gradle.html +:spring-boot-docs: https://docs.spring.io/spring-boot/{version} +:api-documentation: {spring-boot-docs}/gradle-plugin/api +:spring-boot-reference: {spring-boot-docs}/reference/htmlsingle +:build-info-javadoc: {api-documentation}/org/springframework/boot/gradle/tasks/buildinfo/BuildInfo.html +:boot-jar-javadoc: {api-documentation}/org/springframework/boot/gradle/tasks/bundling/BootJar.html +:boot-war-javadoc: {api-documentation}/org/springframework/boot/gradle/tasks/bundling/BootWar.html +:boot-run-javadoc: {api-documentation}/org/springframework/boot/gradle/tasks/run/BootRun.html +:github-code: https://github.com/spring-projects/spring-boot/tree/{github-tag} + + + +[[introduction]] +== Introduction + +The Spring Boot Gradle Plugin provides Spring Boot support in https://gradle.org[Gradle], +allowing you to package executable jar or war archives, run Spring Boot applications, and +use the dependency management provided by `spring-boot-dependencies`. Spring Boot's +Gradle plugin requires Gradle 3.4 or later. + +In addition to this user guide, {api-documentation}[API documentation] is also available. + +include::getting-started.adoc[] +include::managing-dependencies.adoc[] +include::packaging.adoc[] +include::running.adoc[] +include::integrating-with-actuator.adoc[] +include::reacting.adoc[] \ No newline at end of file diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/integrating-with-actuator.adoc b/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/integrating-with-actuator.adoc new file mode 100644 index 00000000000..0460273bbc9 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/integrating-with-actuator.adoc @@ -0,0 +1,55 @@ +[[integrating-with-actuator]] +== Integrating with Actuator + + +[[integrating-with-actuator-build-info]] +=== Generating build information + +Spring Boot Actuator's `info` endpoint automatically publishes information about your +build in the presence of a `META-INF/build-info.properties` file. A +{build-info-javadoc}[`BuildInfo`] task is provided to generate this file. The easiest way +to use the task is via the plugin's DSL: + +[source,groovy,indent=0,subs="verbatim"] +---- +include::../gradle/integrating-with-actuator/build-info-basic.gradle[tags=build-info] +---- + +This will configure a {build-info-javadoc}[`BuildInfo`] task named `bootBuildInfo` and, if +it exists, make the Java plugin's `classes` task depend upon it. The task's desination +directory will be `META-INF` in the output directory of the main source set's resources +(typically `build/resources/main`). + +By default, the generated build information is derived from the project: + +|=== +| Property | Default value + +| `build.artifact` +| The base name of the `bootJar` or `bootWar` task, or `unspecified` if no such task + exists + +| `build.group` +| The group of the project + +| `build.name` +| The name of the project + +| `build.version` +| The version of the project + +|=== + +The properties can be customized using the DSL: + +[source,groovy,indent=0,subs="verbatim"] +---- +include::../gradle/integrating-with-actuator/build-info-custom-values.gradle[tags=custom-values] +---- + +Additional properties can also be added to the build information: + +[source,groovy,indent=0,subs="verbatim"] +---- +include::../gradle/integrating-with-actuator/build-info-additional.gradle[tags=additional] +---- diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/managing-dependencies.adoc b/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/managing-dependencies.adoc new file mode 100644 index 00000000000..31981561b38 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/managing-dependencies.adoc @@ -0,0 +1,46 @@ +[[managing-dependencies]] +== Managing dependencies + +When you apply the {dependency-management-plugin}[`io.spring.dependency-management`] +plugin, Spring Boot's plugin will +automatically <> from the version of Spring Boot that you are using. +This provides a similar dependency management experience to the one that's enjoyed by +Maven users. For example, it allows you to omit version numbers when declaring +dependencies that are managed in the bom. To make use of this functionality, simply +declare dependencies in the usual way but omit the version number: + +[source,groovy,indent=0,subs="verbatim"] +---- +include::../gradle/managing-dependencies/dependencies.gradle[tags=dependencies] +---- + + + +[[managing-dependencies-customizing]] +=== Customizing managed versions + +The `spring-boot-dependencies` bom that is automatically imported when the dependency +management plugin is applied uses properties to control the versions of the dependencies +that it manages. Please refer to the {github-code}/spring-boot-dependencies/pom.xml[bom] +for a complete list of these properties. + +To customize a managed version you set its corresponding property. For example, to +customize the version of SLF4J which is controlled by the `slf4j.version` property: + +[source,groovy,indent=0,subs="verbatim"] +---- +include::../gradle/managing-dependencies/custom-version.gradle[tags=custom-version] +---- + +WARNING: Each Spring Boot release is designed and tested against a specific set of +third-party dependencies. Overriding versions may cause compatibility issues and should +be done with care. + + + +[[managing-dependencies-learning-more]] +=== Learning more + +To learn more about the capabilities of the dependency management plugin, please refer to +its {dependency-management-plugin-documentation}[documentation]. \ No newline at end of file diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/packaging.adoc b/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/packaging.adoc new file mode 100644 index 00000000000..686f20a4eae --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/packaging.adoc @@ -0,0 +1,167 @@ +[[packaging-executable]] +== Packaging executable archives + +The plugin can create executable archives (jar files and war files) that contain all of +an application's dependencies and can then be run with `java -jar`. + + + +[[packaging-executable-jars]] +=== Packaging executable jars + +Executable jars can be built using the `bootJar` task. The task is automatically created +when the `java` plugin is applied and is an instance of {boot-jar-javadoc}[`BootJar`]. + + + +[[packaging-executable-wars]] +=== Packaging executable wars + +Executable wars can be built using the `bootWar` task. The task is automatically created +when the `war` plugin is applied and is an instance of {boot-war-javadoc}[`BootWar`]. + + + +[[packaging-executable-wars-deployable]] +==== Packaging executable and deployable wars + +A war file can be packaged such that it can be executed using `java -jar` and deployed +to an external container. To do so, the embedded servlet container dependencies should +be added to the `providedRuntime` configuration, for example: + +[source,groovy,indent=0,subs="verbatim"] +---- +include::../gradle/packaging/war-container-dependency.gradle[tags=dependencies] +---- + +This ensures that they are package in the war file's `WEB-INF/lib-provided` directory +from where they will not conflict with the external container's own classes. + +NOTE: `providedRuntime` is prefered to Gradle's `compileOnly` configuration as, among +other limitations, `compileOnly` dependencies are not on the test classpath so any +web-based integration tests will fail. + + + +[[packaging-executable-configuring]] +=== Configuring executable archive packaging + +The {boot-jar-javadoc}[`BootJar`] and {boot-war-javadoc}[`BootWar`] tasks are subclasses +of Gradle's `Jar` and `War` tasks respectively. As a result, all of the standard +configuration options that are available when packaging a jar or war are also available +when packaging an executable jar or war. A number of configuration options that are +specific to executable jars and wars are also provided. + + +[[packaging-executable-configuring-main-class]] +==== Configuring the main class + +By default, the executable archive's main class will be configured automatically by +looking for a class with a `public static void main(String[])` method in directories on +the task's classpath. + +The main class can also be configured explicity using the task's `mainClass` property: + +[source,groovy,indent=0,subs="verbatim"] +---- +include::../gradle/packaging/boot-jar-main-class.gradle[tags=main-class] +---- + +Alternatively, if the {application-plugin}[`application` plugin] has been applied +its `mainClassName` project property can be used: + +[source,groovy,indent=0,subs="verbatim"] +---- +include::../gradle/packaging/application-plugin-main-class.gradle[tags=main-class] +---- + +Lastly, the `Start-Class` attribute can be configured on the task's manifest: + +[source,groovy,indent=0,subs="verbatim"] +---- +include::../gradle/packaging/boot-jar-manifest-main-class.gradle[tags=main-class] +---- + + + +[[packaging-executable-configuring-excluding-devtools]] +==== Excluding Devtools + +By default, Spring Boot's Devtools module, +`org.springframework.boot:spring-boot-devtools`, will be excluded from an executable jar +or war. If you want to include Devtools in your archive set the `excludeDevtools` +property to `true`: + +[source,groovy,indent=0,subs="verbatim"] +---- +include::../gradle/packaging/boot-war-include-devtools.gradle[tags=include-devtools] +---- + + + +[[packaging-executable-configuring-unpacking]] +==== Configuring libraries that require unpacking + +Most libraries can be used directly when nested in an executable archive, however certain +libraries can have problems. For example, JRuby includes its own nested jar support which +assumes that `jruby-complete.jar` is always directly available on the file system. + +To deal with any problematic libraries, an executable archive can be configured to unpack +specific nested jars to a temporary folder when the executable archive is run. Libraries +can be identified as requiring unpacking using Ant-style patterns that match against +the absolute path of the source jar file: + +[source,groovy,indent=0,subs="verbatim"] +---- +include::../gradle/packaging/boot-jar-requires-unpack.gradle[tags=requires-unpack] +---- + +For more control a closure can also be used. The closure is passed a `FileTreeElement` +and should return a `boolean` indicating whether or not unpacking is required. + + + +[[packaging-executable-configuring-launch-script]] +==== Making an archive fully executable + +Spring Boot provides support for fully executable archives. An archive is made fully +executable by prepending a shell script that knows how to launch the application. On +Unix-like platforms, this launch script allows the archive to be run directly like any +other executable or to be installed as a service. + +To use this feature, the inclusion of the launch script must be enabled: + +[source,groovy,indent=0,subs="verbatim"] +---- +include::../gradle/packaging/boot-jar-include-launch-script.gradle[tags=include-launch-script] +---- + +This will add Spring Boot's default launch script to the archive. The default launch +script includes several properties with sensible default values. The values can be +customized using the `properties` property: + +[source,groovy,indent=0,subs="verbatim"] +---- +include::../gradle/packaging/boot-jar-launch-script-properties.gradle[tags=launch-script-properties] +---- + +If the default launch script does not meet your needs, the `script` property can be used +to provide a custom launch script: + +[source,groovy,indent=0,subs="verbatim"] +---- +include::../gradle/packaging/boot-jar-custom-launch-script.gradle[tags=custom-launch-script] +---- + + + +[[packaging-executable-configuring-properties-launcher]] +==== Using the `PropertiesLauncher` + +To use the `PropertiesLauncher` to launch an executable jar or war, configure the task's +manifest to set the `Main-Class` attribute: + +[source,groovy,indent=0,subs="verbatim"] +---- +include::../gradle/packaging/boot-war-properties-launcher.gradle[tags=properties-launcher] +---- \ No newline at end of file diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/publishing.adoc b/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/publishing.adoc new file mode 100644 index 00000000000..69be4ed6cef --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/publishing.adoc @@ -0,0 +1,43 @@ +[[publishing-your-application]] +== Publishing your application + + + +[[publishing-your-application-maven]] +=== Publishing with the `maven` plugin + +When the {maven-plugin}[`maven` plugin] is applied, an `Upload` task for the +`bootArchives` configuration named `uploadBootArchives` is automatically created. By +default, the `bootArchives` configuration contains the archive produced by the `bootJar` +or `bootWar` task. The `uploadBootArchives` task can be configured to publish the archive +to a Maven repository: + +[source,groovy,indent=0,subs="verbatim"] +---- +include::../gradle/publishing/maven.gradle[tags=upload] +---- + +[[publishing-your-application-maven-publish]] +=== Publishing with the `maven-publish` plugin + +When the {java-plugin}[`java` plugin] is applied Spring Boot automatically creates a +{software-component}[software component] named `bootJava`. Similarly, when the `war` +plugin is applied, a software component named `bootWeb` is created. `bootJava` contains +the archive produced by the `bootJar` task and `bootWeb` contains the archive provided by +the `bootWar` task. The components can be used with the +{maven-publish-plugin}[`maven-publish` plugin] to publish the archive, for example: + +[source,groovy,indent=0,subs="verbatim"] +---- +include::../gradle/publishing/maven-publish.gradle[tags=publishing] +---- + + + +[[publishing-your-application-distrubution]] +=== Distributing with the `application` plugin + +When the {application-plugin}[`application` plugin] is applied a distribution named +`boot` is created. This distribution contains the archive produced by the `bootJar` or +`bootWar` task and scripts to launch it on Unix-like platforms and Windows. Zip and tar +distributions can be built by the `bootDistZip` and `bootDistTar` tasks respectively. diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/reacting.adoc b/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/reacting.adoc new file mode 100644 index 00000000000..4c5fa9a8c1f --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/reacting.adoc @@ -0,0 +1,78 @@ +[[reacting-to-other-plugins]] +== Reacting to other plugins + +When another plugin is applied the Spring Boot plugin reacts by making various changes +to the project's configuration. This section describes those changes. + +[[reacting-to-other-plugins-java]] +=== Reacting to the Java plugin + +When Gradle's {java-plugin}[`java` plugin] is applied to a project, the Spring Boot +plugin: + +1. Creates a {boot-jar-javadoc}[`BootJar`] task named `bootJar` that will create an + executable, fat jar for the project. The jar will contain everything on the runtime + classpath of the main source set; classes are packaged in `BOOT-INF/classes` and jars + are packaged in `BOOT-INF/lib` +2. Creates a {software-component}[software component] named `bootJava` that contains the + archive produced by the `bootJar` task. +3. Creates a {boot-run-javadoc}[`BootRun`] task named `bootRun` that can be used to run + your application. +4. Creates a configuration named `bootArchives` that contains the artifact produced by + the `bootJar` task. +5. Configures any `JavaCompile` tasks with no configured encoding to use `UTF-8`. + + + +[[reacting-to-other-plugins-war]] +=== Reacting to the war plugin + +When Gradle's {war-plugin}[`war` plugin] is applied to a project, the Spring Boot plugin: + +1. Creates a {boot-war-javadoc}[`BootWar`] task named `bootWar` that will create an + executable, fat war for the project. In addition to the standard packaging, everything + in the `providedRuntime` configuration will be packaged in `WEB-INF/lib-provided`. +2. Creates a {software-component}[software component] named `bootWeb` that contains the + archive produced by the `bootWar` task. +3. Configures the `bootArchives` configuration to contain the artifact produced by the + `bootWar` task. + + + +[[reacting-to-other-plugins-dependency-management]] +=== Reacting to the dependency management plugin + +When the {dependency-management-plugin}[`io.spring.dependency-management` plugin] is +applied to a project, the Spring Boot plugin will automatically import the +`spring-boot-dependencies` bom. + + + +[[reacting-to-other-plugins-application]] +=== Reacting to the application plugin + +When Gradle's {application-plugin}[`application` plugin] is applied to a project, the +Spring Boot plugin: + +1. Creates a `CreateStartScripts` task named `bootStartScripts` that will creates scripts + that launch the artifact in the `bootArchives` configuration using `java -jar`. +2. Creates a new distribution named `boot` and configures it to contain the artifact in + the `bootArchives` configuration in its `lib` directory and the start scripts in its + `bin` directory. +3. Configures the `bootRun` task to use the `mainClassName` property as a convention for + its `main` property. +4. Configures the `bootRun` task to use the `applicationDefaultJvmArgs` property as a + convention for its `jvmArgs` property. +5. Configures the `bootJar` task to use the `mainClassName` property as a convention for + the `Start-Class` entry in its manifest. +6. Configures the `bootWar` task to use the `mainClassName` property as a convention for + the `Start-Class` entry in its manifest. + + + +[[reacting-to-other-plugins-maven]] +=== Reacting to the Maven plugin + +When Gradle's {maven-plugin}[`maven` plugin] is applied to a project, the Spring Boot +plugin will configure the `uploadBootArchives` `Upload` task to ensure that no +dependencies are declared in the pom that it generates. diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/running.adoc b/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/running.adoc new file mode 100644 index 00000000000..5a8f0cb1ece --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/running.adoc @@ -0,0 +1,49 @@ +[[running-your-application]] +== Running your application with Gradle + +To run your application without first building an archive use the `bootRun` task: + +[source,bash,indent=0,subs="verbatim"] +---- + $ ./gradlew bootRun +---- + +The `bootRun` task is an instance of +{boot-run-javadoc}[`BootRun`] which is a `JavaExec` subclass. As such, all of the +{gradle-dsl}/org.gradle.api.tasks.JavaExec.html[usual configuration options] for executing +a Java process in Gradle are available to you. The task is automatically configured to use +the runtime classpath of the main source set. + +By default, the main class will be configured automatically by looking for a class with a +`public static void main(String[])` method in directories on the task's classpath. + +The main class can also be configured explicity using the task's `main` property: + +[source,groovy,indent=0,subs="verbatim"] +---- +include::../gradle/running/boot-run-main.gradle[tags=main] +---- + +Alternatively, if the {application-plugin}[`application` plugin] has been applied +its `mainClassName` project property can be used: + +[source,groovy,indent=0,subs="verbatim"] +---- +include::../gradle/running/application-plugin-main-class-name.gradle[tags=main-class] +---- + + + +[[running-your-application-reloading-resources]] +=== Reloading resources +If devtools has been added to your project it will automatically monitor your +application for changes. Alternatively, you can configure `bootRun` such that your +application's static resources are loaded from their source location: + +[source,groovy,indent=0,subs="verbatim"] +---- +include::../gradle/running/boot-run-source-resources.gradle[tags=source-resources] +---- + +This makes them reloadable in the live application which can be helpful at development +time. diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/getting-started/apply-plugin-milestone.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/getting-started/apply-plugin-milestone.gradle new file mode 100644 index 00000000000..0302ec6f4de --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/getting-started/apply-plugin-milestone.gradle @@ -0,0 +1,11 @@ +buildscript { + repositories { + maven { url 'https://repo.spring.io/libs-milestone' } + } + + dependencies { + classpath 'org.springframework.boot:spring-boot-gradle-plugin:{version}' + } +} + +apply plugin: 'org.springframework.boot' diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/getting-started/apply-plugin-release.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/getting-started/apply-plugin-release.gradle new file mode 100644 index 00000000000..cf13509ffdd --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/getting-started/apply-plugin-release.gradle @@ -0,0 +1,3 @@ +plugins { + id 'org.springframework.boot' version '{version}' +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/getting-started/apply-plugin-snapshot.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/getting-started/apply-plugin-snapshot.gradle new file mode 100644 index 00000000000..c775fa09252 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/getting-started/apply-plugin-snapshot.gradle @@ -0,0 +1,11 @@ +buildscript { + repositories { + maven { url 'https://repo.spring.io/libs-snapshot' } + } + + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/getting-started/typical-plugins.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/getting-started/typical-plugins.gradle new file mode 100644 index 00000000000..6549d2bd3e4 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/getting-started/typical-plugins.gradle @@ -0,0 +1,17 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +// tag::apply[] +apply plugin: 'java' +apply plugin: 'io.spring.dependency-management' +// end::apply[] + +task verify { + doLast { + plugins.getPlugin(org.gradle.api.plugins.JavaPlugin.class) + plugins.getPlugin(io.spring.gradle.dependencymanagement.DependencyManagementPlugin.class) + } +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/integrating-with-actuator/build-info-additional.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/integrating-with-actuator/build-info-additional.gradle new file mode 100644 index 00000000000..a70995ea380 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/integrating-with-actuator/build-info-additional.gradle @@ -0,0 +1,21 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'java' + +// tag::additional[] +springBoot { + buildInfo { + properties { + additional = [ + 'a': 'alpha', + 'b': 'bravo' + ] + } + } +} +// end::additional[] diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/integrating-with-actuator/build-info-basic.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/integrating-with-actuator/build-info-basic.gradle new file mode 100644 index 00000000000..43329cdd618 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/integrating-with-actuator/build-info-basic.gradle @@ -0,0 +1,14 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'java' + +// tag::build-info[] +springBoot { + buildInfo() +} +// end::build-info[] diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/integrating-with-actuator/build-info-custom-values.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/integrating-with-actuator/build-info-custom-values.gradle new file mode 100644 index 00000000000..c8e8b0b4168 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/integrating-with-actuator/build-info-custom-values.gradle @@ -0,0 +1,21 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'java' + +// tag::custom-values[] +springBoot { + buildInfo { + properties { + artifact = 'example-app' + version = '1.2.3' + group = 'com.example' + name = 'Example application' + } + } +} +// end::custom-values[] diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/managing-dependencies/custom-version.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/managing-dependencies/custom-version.gradle new file mode 100644 index 00000000000..54009ee8c20 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/managing-dependencies/custom-version.gradle @@ -0,0 +1,32 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'io.spring.dependency-management' + +dependencyManagement { + resolutionStrategy { + eachDependency { + if (it.requested.group == 'org.springframework.boot') { + it.useVersion project.bootVersion + } + } + } +} + +// tag::custom-version[] +ext['slf4j.version'] = '1.7.20' +// end::custom-version[] + +repositories { + mavenLocal() +} + +task slf4jVersion { + doLast { + println dependencyManagement.managedVersions['org.slf4j:slf4j-api'] + } +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/managing-dependencies/dependencies.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/managing-dependencies/dependencies.gradle new file mode 100644 index 00000000000..ecbf6ffedc1 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/managing-dependencies/dependencies.gradle @@ -0,0 +1,19 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +plugins { + id 'java' +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'io.spring.dependency-management' + +// tag::dependencies[] +dependencies { + compile 'org.springframework.boot:spring-boot-starter-web' + compile 'org.springframework.boot:spring-boot-starter-data-jpa' +} +// end::dependencies[] diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/application-plugin-main-class.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/application-plugin-main-class.gradle new file mode 100644 index 00000000000..b91321d8fde --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/application-plugin-main-class.gradle @@ -0,0 +1,13 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'java' +apply plugin: 'application' + +// tag::main-class[] +mainClassName = 'com.example.ExampleApplication' +// end::main-class[] diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-jar-custom-launch-script.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-jar-custom-launch-script.gradle new file mode 100644 index 00000000000..c813b9a134f --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-jar-custom-launch-script.gradle @@ -0,0 +1,21 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'java' + +bootJar { + mainClass 'com.example.ExampleApplication' +} + +// tag::custom-launch-script[] +bootJar { + launchScript { + included = true + script = file('src/custom.script') + } +} +// end::custom-launch-script[] diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-jar-include-launch-script.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-jar-include-launch-script.gradle new file mode 100644 index 00000000000..6df82aeb198 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-jar-include-launch-script.gradle @@ -0,0 +1,20 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'java' + +bootJar { + mainClass 'com.example.ExampleApplication' +} + +// tag::include-launch-script[] +bootJar { + launchScript { + included = true + } +} +// end::include-launch-script[] diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-jar-launch-script-properties.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-jar-launch-script-properties.gradle new file mode 100644 index 00000000000..1de01e52b5b --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-jar-launch-script-properties.gradle @@ -0,0 +1,21 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'java' + +bootJar { + mainClass 'com.example.ExampleApplication' +} + +// tag::launch-script-properties[] +bootJar { + launchScript { + included = true + properties 'logFilename': 'example-app.log' + } +} +// end::launch-script-properties[] diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-jar-main-class.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-jar-main-class.gradle new file mode 100644 index 00000000000..b3ce53b9ec7 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-jar-main-class.gradle @@ -0,0 +1,14 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'java' + +// tag::main-class[] +bootJar { + mainClass = 'com.example.ExampleApplication' +} +// end::main-class[] diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-jar-manifest-main-class.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-jar-manifest-main-class.gradle new file mode 100644 index 00000000000..557242e3f1e --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-jar-manifest-main-class.gradle @@ -0,0 +1,16 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'java' + +// tag::main-class[] +bootJar { + manifest { + attributes 'Start-Class': 'com.example.ExampleApplication' + } +} +// end::main-class[] diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-jar-requires-unpack.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-jar-requires-unpack.gradle new file mode 100644 index 00000000000..e21da8d831e --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-jar-requires-unpack.gradle @@ -0,0 +1,26 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'java' + +repositories { + mavenCentral() +} + +dependencies { + runtime 'org.jruby:jruby-complete:1.7.25' +} + +bootJar { + mainClass 'com.example.ExampleApplication' +} + +// tag::requires-unpack[] +bootJar { + requiresUnpack '**/jruby-complete-*.jar' +} +// end::requires-unpack[] diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-war-include-devtools.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-war-include-devtools.gradle new file mode 100644 index 00000000000..a739250d3bc --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-war-include-devtools.gradle @@ -0,0 +1,19 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'war' + +bootWar { + mainClass 'com.example.ExampleApplication' + classpath file('spring-boot-devtools-1.2.3.RELEASE.jar') +} + +// tag::include-devtools[] +bootWar { + excludeDevtools = false +} +// end::include-devtools[] diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-war-properties-launcher.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-war-properties-launcher.gradle new file mode 100644 index 00000000000..a2d915d9051 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-war-properties-launcher.gradle @@ -0,0 +1,20 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'war' + +bootWar { + mainClass 'com.example.ExampleApplication' +} + +// tag::properties-launcher[] +bootWar { + manifest { + attributes 'Main-Class': 'org.springframework.boot.loader.PropertiesLauncher' + } +} +// end::properties-launcher[] diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/war-container-dependency.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/war-container-dependency.gradle new file mode 100644 index 00000000000..9f8c91b64e6 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/war-container-dependency.gradle @@ -0,0 +1,19 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +plugins { + id 'war' +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'io.spring.dependency-management' + +// tag::dependencies[] +dependencies { + compile 'org.springframework.boot:spring-boot-starter-web' + providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat' +} +// end::dependencies[] diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/publishing/maven-publish.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/publishing/maven-publish.gradle new file mode 100644 index 00000000000..61ded0c3578 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/publishing/maven-publish.gradle @@ -0,0 +1,31 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'java' +apply plugin: 'maven-publish' + +// tag::publishing[] +publishing { + publications { + bootJava(MavenPublication) { + from components.bootJava + } + } + repositories { + maven { + url 'https://repo.example.com' + } + } +} +// end::publishing[] + +task publishingConfiguration { + doLast { + println publishing.publications.bootJava + println publishing.repositories.maven.url + } +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/publishing/maven.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/publishing/maven.gradle new file mode 100644 index 00000000000..8adefa06431 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/publishing/maven.gradle @@ -0,0 +1,25 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'java' +apply plugin: 'maven' + +// tag::upload[] +uploadBootArchives { + repositories { + mavenDeployer { + repository url: 'https://repo.example.com' + } + } +} +// end::upload[] + +task deployerRepository { + doLast { + println uploadBootArchives.repositories.mavenDeployer.repository.url + } +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/running/application-plugin-main-class-name.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/running/application-plugin-main-class-name.gradle new file mode 100644 index 00000000000..6e05ceaa3df --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/running/application-plugin-main-class-name.gradle @@ -0,0 +1,19 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'java' +apply plugin: 'application' + +// tag::main-class[] +mainClassName = 'com.example.ExampleApplication' +// end::main-class[] + +task configuredMainClass { + doLast { + println bootRun.main + } +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/running/boot-run-main.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/running/boot-run-main.gradle new file mode 100644 index 00000000000..82907a4c4e0 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/running/boot-run-main.gradle @@ -0,0 +1,20 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'java' + +// tag::main[] +bootRun { + main = 'com.example.ExampleApplication' +} +// end::main[] + +task configuredMainClass { + doLast { + println bootRun.main + } +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/running/boot-run-source-resources.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/running/boot-run-source-resources.gradle new file mode 100644 index 00000000000..19b016b5263 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/running/boot-run-source-resources.gradle @@ -0,0 +1,20 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'java' + +// tag::source-resources[] +bootRun { + sourceResources sourceSets.main +} +// end::source-resources[] + +task configuredClasspath { + doLast { + println bootRun.classpath.files + } +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/javadoc/spring-javadoc.css b/spring-boot-tools/spring-boot-gradle-plugin/src/main/javadoc/spring-javadoc.css new file mode 100644 index 00000000000..06ad42277c6 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/javadoc/spring-javadoc.css @@ -0,0 +1,599 @@ +/* Javadoc style sheet */ +/* +Overall document style +*/ + +@import url('resources/fonts/dejavu.css'); + +body { + background-color:#ffffff; + color:#353833; + font-family:'DejaVu Sans', Arial, Helvetica, sans-serif; + font-size:14px; + margin:0; +} +a:link, a:visited { + text-decoration:none; + color:#4A6782; +} +a:hover, a:focus { + text-decoration:none; + color:#bb7a2a; +} +a:active { + text-decoration:none; + color:#4A6782; +} +a[name] { + color:#353833; +} +a[name]:hover { + text-decoration:none; + color:#353833; +} +pre { + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; +} +h1 { + font-size:20px; +} +h2 { + font-size:18px; +} +h3 { + font-size:16px; + font-style:italic; +} +h4 { + font-size:13px; +} +h5 { + font-size:12px; +} +h6 { + font-size:11px; +} +ul { + list-style-type:disc; +} +code, tt { + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; + padding-top:4px; + margin-top:8px; + line-height:1.4em; +} +dt code { + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; + padding-top:4px; +} +table tr td dt code { + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; + vertical-align:top; + padding-top:4px; +} +sup { + font-size:8px; +} +/* +Document title and Copyright styles +*/ +.clear { + clear:both; + height:0px; + overflow:hidden; +} +.aboutLanguage { + float:right; + padding:0px 21px; + font-size:11px; + z-index:200; + margin-top:-9px; +} +.legalCopy { + margin-left:.5em; +} +.bar a, .bar a:link, .bar a:visited, .bar a:active { + color:#FFFFFF; + text-decoration:none; +} +.bar a:hover, .bar a:focus { + color:#bb7a2a; +} +.tab { + background-color:#0066FF; + color:#ffffff; + padding:8px; + width:5em; + font-weight:bold; +} +/* +Navigation bar styles +*/ +.bar { + background-color:#4D7A97; + color:#FFFFFF; + padding:.8em .5em .4em .8em; + height:auto;/*height:1.8em;*/ + font-size:11px; + margin:0; +} +.topNav { + background-color:#4D7A97; + color:#FFFFFF; + float:left; + padding:0; + width:100%; + clear:right; + height:2.8em; + padding-top:10px; + overflow:hidden; + font-size:12px; +} +.bottomNav { + margin-top:10px; + background-color:#4D7A97; + color:#FFFFFF; + float:left; + padding:0; + width:100%; + clear:right; + height:2.8em; + padding-top:10px; + overflow:hidden; + font-size:12px; +} +.subNav { + background-color:#dee3e9; + float:left; + width:100%; + overflow:hidden; + font-size:12px; +} +.subNav div { + clear:left; + float:left; + padding:0 0 5px 6px; + text-transform:uppercase; +} +ul.navList, ul.subNavList { + float:left; + margin:0 25px 0 0; + padding:0; +} +ul.navList li{ + list-style:none; + float:left; + padding: 5px 6px; + text-transform:uppercase; +} +ul.subNavList li{ + list-style:none; + float:left; +} +.topNav a:link, .topNav a:active, .topNav a:visited, .bottomNav a:link, .bottomNav a:active, .bottomNav a:visited { + color:#FFFFFF; + text-decoration:none; + text-transform:uppercase; +} +.topNav a:hover, .bottomNav a:hover { + text-decoration:none; + color:#bb7a2a; + text-transform:uppercase; +} +.navBarCell1Rev { + background-color:#F8981D; + color:#253441; + margin: auto 5px; +} +.skipNav { + position:absolute; + top:auto; + left:-9999px; + overflow:hidden; +} +/* +Page header and footer styles +*/ +.header, .footer { + clear:both; + margin:0 20px; + padding:5px 0 0 0; +} +.indexHeader { + margin:10px; + position:relative; +} +.indexHeader span{ + margin-right:15px; +} +.indexHeader h1 { + font-size:13px; +} +.title { + color:#2c4557; + margin:10px 0; +} +.subTitle { + margin:5px 0 0 0; +} +.header ul { + margin:0 0 15px 0; + padding:0; +} +.footer ul { + margin:20px 0 5px 0; +} +.header ul li, .footer ul li { + list-style:none; + font-size:13px; +} +/* +Heading styles +*/ +div.details ul.blockList ul.blockList ul.blockList li.blockList h4, div.details ul.blockList ul.blockList ul.blockListLast li.blockList h4 { + background-color:#dee3e9; + border:1px solid #d0d9e0; + margin:0 0 6px -8px; + padding:7px 5px; +} +ul.blockList ul.blockList ul.blockList li.blockList h3 { + background-color:#dee3e9; + border:1px solid #d0d9e0; + margin:0 0 6px -8px; + padding:7px 5px; +} +ul.blockList ul.blockList li.blockList h3 { + padding:0; + margin:15px 0; +} +ul.blockList li.blockList h2 { + padding:0px 0 20px 0; +} +/* +Page layout container styles +*/ +.contentContainer, .sourceContainer, .classUseContainer, .serializedFormContainer, .constantValuesContainer { + clear:both; + padding:10px 20px; + position:relative; +} +.indexContainer { + margin:10px; + position:relative; + font-size:12px; +} +.indexContainer h2 { + font-size:13px; + padding:0 0 3px 0; +} +.indexContainer ul { + margin:0; + padding:0; +} +.indexContainer ul li { + list-style:none; + padding-top:2px; +} +.contentContainer .description dl dt, .contentContainer .details dl dt, .serializedFormContainer dl dt { + font-size:12px; + font-weight:bold; + margin:10px 0 0 0; + color:#4E4E4E; +} +.contentContainer .description dl dd, .contentContainer .details dl dd, .serializedFormContainer dl dd { + margin:5px 0 10px 0px; + font-size:14px; + font-family:'DejaVu Sans Mono',monospace; +} +.serializedFormContainer dl.nameValue dt { + margin-left:1px; + font-size:1.1em; + display:inline; + font-weight:bold; +} +.serializedFormContainer dl.nameValue dd { + margin:0 0 0 1px; + font-size:1.1em; + display:inline; +} +/* +List styles +*/ +ul.horizontal li { + display:inline; + font-size:0.9em; +} +ul.inheritance { + margin:0; + padding:0; +} +ul.inheritance li { + display:inline; + list-style:none; +} +ul.inheritance li ul.inheritance { + margin-left:15px; + padding-left:15px; + padding-top:1px; +} +ul.blockList, ul.blockListLast { + margin:10px 0 10px 0; + padding:0; +} +ul.blockList li.blockList, ul.blockListLast li.blockList { + list-style:none; + margin-bottom:15px; + line-height:1.4; +} +ul.blockList ul.blockList li.blockList, ul.blockList ul.blockListLast li.blockList { + padding:0px 20px 5px 10px; + border:1px solid #ededed; + background-color:#f8f8f8; +} +ul.blockList ul.blockList ul.blockList li.blockList, ul.blockList ul.blockList ul.blockListLast li.blockList { + padding:0 0 5px 8px; + background-color:#ffffff; + border:none; +} +ul.blockList ul.blockList ul.blockList ul.blockList li.blockList { + margin-left:0; + padding-left:0; + padding-bottom:15px; + border:none; +} +ul.blockList ul.blockList ul.blockList ul.blockList li.blockListLast { + list-style:none; + border-bottom:none; + padding-bottom:0; +} +table tr td dl, table tr td dl dt, table tr td dl dd { + margin-top:0; + margin-bottom:1px; +} +/* +Table styles +*/ +.overviewSummary, .memberSummary, .typeSummary, .useSummary, .constantsSummary, .deprecatedSummary { + width:100%; + border-left:1px solid #EEE; + border-right:1px solid #EEE; + border-bottom:1px solid #EEE; +} +.overviewSummary, .memberSummary { + padding:0px; +} +.overviewSummary caption, .memberSummary caption, .typeSummary caption, +.useSummary caption, .constantsSummary caption, .deprecatedSummary caption { + position:relative; + text-align:left; + background-repeat:no-repeat; + color:#253441; + font-weight:bold; + clear:none; + overflow:hidden; + padding:0px; + padding-top:10px; + padding-left:1px; + margin:0px; + white-space:pre; +} +.overviewSummary caption a:link, .memberSummary caption a:link, .typeSummary caption a:link, +.useSummary caption a:link, .constantsSummary caption a:link, .deprecatedSummary caption a:link, +.overviewSummary caption a:hover, .memberSummary caption a:hover, .typeSummary caption a:hover, +.useSummary caption a:hover, .constantsSummary caption a:hover, .deprecatedSummary caption a:hover, +.overviewSummary caption a:active, .memberSummary caption a:active, .typeSummary caption a:active, +.useSummary caption a:active, .constantsSummary caption a:active, .deprecatedSummary caption a:active, +.overviewSummary caption a:visited, .memberSummary caption a:visited, .typeSummary caption a:visited, +.useSummary caption a:visited, .constantsSummary caption a:visited, .deprecatedSummary caption a:visited { + color:#FFFFFF; +} +.overviewSummary caption span, .memberSummary caption span, .typeSummary caption span, +.useSummary caption span, .constantsSummary caption span, .deprecatedSummary caption span { + white-space:nowrap; + padding-top:5px; + padding-left:12px; + padding-right:12px; + padding-bottom:7px; + display:inline-block; + float:left; + background-color:#F8981D; + border: none; + height:16px; +} +.memberSummary caption span.activeTableTab span { + white-space:nowrap; + padding-top:5px; + padding-left:12px; + padding-right:12px; + margin-right:3px; + display:inline-block; + float:left; + background-color:#F8981D; + height:16px; +} +.memberSummary caption span.tableTab span { + white-space:nowrap; + padding-top:5px; + padding-left:12px; + padding-right:12px; + margin-right:3px; + display:inline-block; + float:left; + background-color:#4D7A97; + height:16px; +} +.memberSummary caption span.tableTab, .memberSummary caption span.activeTableTab { + padding-top:0px; + padding-left:0px; + padding-right:0px; + background-image:none; + float:none; + display:inline; +} +.overviewSummary .tabEnd, .memberSummary .tabEnd, .typeSummary .tabEnd, +.useSummary .tabEnd, .constantsSummary .tabEnd, .deprecatedSummary .tabEnd { + display:none; + width:5px; + position:relative; + float:left; + background-color:#F8981D; +} +.memberSummary .activeTableTab .tabEnd { + display:none; + width:5px; + margin-right:3px; + position:relative; + float:left; + background-color:#F8981D; +} +.memberSummary .tableTab .tabEnd { + display:none; + width:5px; + margin-right:3px; + position:relative; + background-color:#4D7A97; + float:left; + +} +.overviewSummary td, .memberSummary td, .typeSummary td, +.useSummary td, .constantsSummary td, .deprecatedSummary td { + text-align:left; + padding:0px 0px 12px 10px; + width:100%; +} +th.colOne, th.colFirst, th.colLast, .useSummary th, .constantsSummary th, +td.colOne, td.colFirst, td.colLast, .useSummary td, .constantsSummary td{ + vertical-align:top; + padding-right:0px; + padding-top:8px; + padding-bottom:3px; +} +th.colFirst, th.colLast, th.colOne, .constantsSummary th { + background:#dee3e9; + text-align:left; + padding:8px 3px 3px 7px; +} +td.colFirst, th.colFirst { + white-space:nowrap; + font-size:13px; +} +td.colLast, th.colLast { + font-size:13px; +} +td.colOne, th.colOne { + font-size:13px; +} +.overviewSummary td.colFirst, .overviewSummary th.colFirst, +.overviewSummary td.colOne, .overviewSummary th.colOne, +.memberSummary td.colFirst, .memberSummary th.colFirst, +.memberSummary td.colOne, .memberSummary th.colOne, +.typeSummary td.colFirst{ + width:25%; + vertical-align:top; +} +td.colOne a:link, td.colOne a:active, td.colOne a:visited, td.colOne a:hover, td.colFirst a:link, td.colFirst a:active, td.colFirst a:visited, td.colFirst a:hover, td.colLast a:link, td.colLast a:active, td.colLast a:visited, td.colLast a:hover, .constantValuesContainer td a:link, .constantValuesContainer td a:active, .constantValuesContainer td a:visited, .constantValuesContainer td a:hover { + font-weight:bold; +} +.tableSubHeadingColor { + background-color:#EEEEFF; +} +.altColor { + background-color:#FFFFFF; +} +.rowColor { + background-color:#EEEEEF; +} +/* +Content styles +*/ +.description pre { + margin-top:0; +} +.deprecatedContent { + margin:0; + padding:10px 0; +} +.docSummary { + padding:0; +} + +ul.blockList ul.blockList ul.blockList li.blockList h3 { + font-style:normal; +} + +div.block { + font-size:14px; + font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif; +} + +td.colLast div { + padding-top:0px; +} + + +td.colLast a { + padding-bottom:3px; +} +/* +Formatting effect styles +*/ +.sourceLineNo { + color:green; + padding:0 30px 0 0; +} +h1.hidden { + visibility:hidden; + overflow:hidden; + font-size:10px; +} +.block { + display:block; + margin:3px 10px 2px 0px; + color:#474747; +} +.deprecatedLabel, .descfrmTypeLabel, .memberNameLabel, .memberNameLink, +.overrideSpecifyLabel, .packageHierarchyLabel, .paramLabel, .returnLabel, +.seeLabel, .simpleTagLabel, .throwsLabel, .typeNameLabel, .typeNameLink { + font-weight:bold; +} +.deprecationComment, .emphasizedPhrase, .interfaceName { + font-style:italic; +} + +div.block div.block span.deprecationComment, div.block div.block span.emphasizedPhrase, +div.block div.block span.interfaceName { + font-style:normal; +} + +div.contentContainer ul.blockList li.blockList h2{ + padding-bottom:0px; +} + + + +/* +Spring +*/ + +pre.code { + background-color: #F8F8F8; + border: 1px solid #CCCCCC; + border-radius: 3px 3px 3px 3px; + overflow: auto; + padding: 10px; + margin: 4px 20px 2px 0px; +} + +pre.code code, pre.code code * { + font-size: 1em; +} + +pre.code code, pre.code code * { + padding: 0 !important; + margin: 0 !important; +} + diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/GettingStartedDocumentationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/GettingStartedDocumentationTests.java new file mode 100644 index 00000000000..4c05d9f646e --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/GettingStartedDocumentationTests.java @@ -0,0 +1,47 @@ +/* + * Copyright 2012-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.boot.gradle.docs; + +import org.junit.Rule; +import org.junit.Test; + +import org.springframework.boot.gradle.testkit.GradleBuild; + +/** + * Tests for the getting started documentation. + * + * @author Andy Wilkinson + */ +public class GettingStartedDocumentationTests { + + @Rule + public GradleBuild gradleBuild = new GradleBuild(); + + @Test + public void applyPluginSnapshotExampleEvaluatesSuccessfully() { + this.gradleBuild + .script("src/main/gradle/getting-started/apply-plugin-snapshot.gradle") + .build(); + } + + @Test + public void typicalPluginsAppliesExceptedPlugins() { + this.gradleBuild.script("src/main/gradle/getting-started/typical-plugins.gradle") + .build("verify"); + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/IntegratingWithActuatorDocumentationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/IntegratingWithActuatorDocumentationTests.java new file mode 100644 index 00000000000..8ba0219bc20 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/IntegratingWithActuatorDocumentationTests.java @@ -0,0 +1,90 @@ +/* + * Copyright 2012-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.boot.gradle.docs; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.Properties; + +import org.junit.Rule; +import org.junit.Test; + +import org.springframework.boot.gradle.testkit.GradleBuild; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for the generating build info documentation. + * + * @author Andy Wilkinson + */ +public class IntegratingWithActuatorDocumentationTests { + + @Rule + public GradleBuild gradleBuild = new GradleBuild(); + + @Test + public void basicBuildInfo() throws IOException { + this.gradleBuild + .script("src/main/gradle/integrating-with-actuator/build-info-basic.gradle") + .build("bootBuildInfo"); + assertThat(new File(this.gradleBuild.getProjectDir(), + "build/resources/main/META-INF/build-info.properties")).isFile(); + } + + @Test + public void buildInfoCustomValues() throws IOException { + this.gradleBuild + .script("src/main/gradle/integrating-with-actuator/build-info-custom-values.gradle") + .build("bootBuildInfo"); + File file = new File(this.gradleBuild.getProjectDir(), + "build/resources/main/META-INF/build-info.properties"); + assertThat(file).isFile(); + Properties properties = buildInfoProperties(file); + assertThat(properties).containsEntry("build.artifact", "example-app"); + assertThat(properties).containsEntry("build.version", "1.2.3"); + assertThat(properties).containsEntry("build.group", "com.example"); + assertThat(properties).containsEntry("build.name", "Example application"); + } + + @Test + public void buildInfoAdditional() throws IOException { + this.gradleBuild + .script("src/main/gradle/integrating-with-actuator/build-info-additional.gradle") + .build("bootBuildInfo"); + File file = new File(this.gradleBuild.getProjectDir(), + "build/resources/main/META-INF/build-info.properties"); + assertThat(file).isFile(); + Properties properties = buildInfoProperties(file); + assertThat(properties).containsEntry("build.a", "alpha"); + assertThat(properties).containsEntry("build.b", "bravo"); + } + + private Properties buildInfoProperties(File file) { + assertThat(file).isFile(); + Properties properties = new Properties(); + try (FileReader reader = new FileReader(file)) { + properties.load(reader); + return properties; + } + catch (IOException ex) { + throw new RuntimeException(ex); + } + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/ManagingDependenciesDocumentationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/ManagingDependenciesDocumentationTests.java new file mode 100644 index 00000000000..0d69c8a0b6f --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/ManagingDependenciesDocumentationTests.java @@ -0,0 +1,50 @@ +/* + * Copyright 2012-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.boot.gradle.docs; + +import org.junit.Rule; +import org.junit.Test; + +import org.springframework.boot.gradle.testkit.GradleBuild; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for the managing dependencies documentation. + * + * @author Andy Wilkinson + */ +public class ManagingDependenciesDocumentationTests { + + @Rule + public GradleBuild gradleBuild = new GradleBuild(); + + @Test + public void dependenciesExampleEvaluatesSuccessfully() { + this.gradleBuild + .script("src/main/gradle/managing-dependencies/dependencies.gradle") + .build(); + } + + @Test + public void customManagedVersions() { + assertThat(this.gradleBuild + .script("src/main/gradle/managing-dependencies/custom-version.gradle") + .build("slf4jVersion").getOutput()).contains("1.7.20"); + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/PackagingDocumentationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/PackagingDocumentationTests.java new file mode 100644 index 00000000000..03afebb19c7 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/PackagingDocumentationTests.java @@ -0,0 +1,176 @@ +/* + * Copyright 2012-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.boot.gradle.docs; + +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +import org.junit.Rule; +import org.junit.Test; + +import org.springframework.boot.gradle.testkit.GradleBuild; +import org.springframework.util.FileCopyUtils; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for the packaging documentation. + * + * @author Andy Wilkinson + */ +public class PackagingDocumentationTests { + + @Rule + public GradleBuild gradleBuild = new GradleBuild(); + + @Test + public void warContainerDependencyEvaluatesSuccessfully() { + this.gradleBuild + .script("src/main/gradle/packaging/war-container-dependency.gradle") + .build(); + } + + @Test + public void bootJarMainClass() throws IOException { + this.gradleBuild.script("src/main/gradle/packaging/boot-jar-main-class.gradle") + .build("bootJar"); + File file = new File(this.gradleBuild.getProjectDir(), + "build/libs/" + this.gradleBuild.getProjectDir().getName() + ".jar"); + assertThat(file).isFile(); + try (JarFile jar = new JarFile(file)) { + assertThat(jar.getManifest().getMainAttributes().getValue("Start-Class")) + .isEqualTo("com.example.ExampleApplication"); + } + } + + @Test + public void bootJarManifestMainClass() throws IOException { + this.gradleBuild + .script("src/main/gradle/packaging/boot-jar-manifest-main-class.gradle") + .build("bootJar"); + File file = new File(this.gradleBuild.getProjectDir(), + "build/libs/" + this.gradleBuild.getProjectDir().getName() + ".jar"); + assertThat(file).isFile(); + try (JarFile jar = new JarFile(file)) { + assertThat(jar.getManifest().getMainAttributes().getValue("Start-Class")) + .isEqualTo("com.example.ExampleApplication"); + } + } + + @Test + public void applicationPluginMainClass() throws IOException { + this.gradleBuild + .script("src/main/gradle/packaging/application-plugin-main-class.gradle") + .build("bootJar"); + File file = new File(this.gradleBuild.getProjectDir(), + "build/libs/" + this.gradleBuild.getProjectDir().getName() + ".jar"); + assertThat(file).isFile(); + try (JarFile jar = new JarFile(file)) { + assertThat(jar.getManifest().getMainAttributes().getValue("Start-Class")) + .isEqualTo("com.example.ExampleApplication"); + } + } + + @Test + public void bootWarIncludeDevtools() throws IOException { + new File(this.gradleBuild.getProjectDir(), + "spring-boot-devtools-1.2.3.RELEASE.jar").createNewFile(); + this.gradleBuild + .script("src/main/gradle/packaging/boot-war-include-devtools.gradle") + .build("bootWar"); + File file = new File(this.gradleBuild.getProjectDir(), + "build/libs/" + this.gradleBuild.getProjectDir().getName() + ".war"); + assertThat(file).isFile(); + try (JarFile jar = new JarFile(file)) { + assertThat(jar.getEntry("WEB-INF/lib/spring-boot-devtools-1.2.3.RELEASE.jar")) + .isNotNull(); + } + } + + @Test + public void bootJarRequiresUnpack() throws IOException { + this.gradleBuild + .script("src/main/gradle/packaging/boot-jar-requires-unpack.gradle") + .build("bootJar"); + File file = new File(this.gradleBuild.getProjectDir(), + "build/libs/" + this.gradleBuild.getProjectDir().getName() + ".jar"); + assertThat(file).isFile(); + try (JarFile jar = new JarFile(file)) { + JarEntry entry = jar.getJarEntry("BOOT-INF/lib/jruby-complete-1.7.25.jar"); + assertThat(entry).isNotNull(); + assertThat(entry.getComment()).startsWith("UNPACK:"); + } + } + + @Test + public void bootJarIncludeLaunchScript() throws IOException { + this.gradleBuild + .script("src/main/gradle/packaging/boot-jar-include-launch-script.gradle") + .build("bootJar"); + File file = new File(this.gradleBuild.getProjectDir(), + "build/libs/" + this.gradleBuild.getProjectDir().getName() + ".jar"); + assertThat(file).isFile(); + assertThat(FileCopyUtils.copyToString(new FileReader(file))) + .startsWith("#!/bin/bash"); + } + + @Test + public void bootJarLaunchScriptProperties() throws IOException { + this.gradleBuild + .script("src/main/gradle/packaging/boot-jar-launch-script-properties.gradle") + .build("bootJar"); + File file = new File(this.gradleBuild.getProjectDir(), + "build/libs/" + this.gradleBuild.getProjectDir().getName() + ".jar"); + assertThat(file).isFile(); + assertThat(FileCopyUtils.copyToString(new FileReader(file))) + .contains("example-app.log"); + } + + @Test + public void bootJarCustomLaunchScript() throws IOException { + File customScriptFile = new File(this.gradleBuild.getProjectDir(), + "src/custom.script"); + customScriptFile.getParentFile().mkdirs(); + FileCopyUtils.copy("custom", new FileWriter(customScriptFile)); + this.gradleBuild + .script("src/main/gradle/packaging/boot-jar-custom-launch-script.gradle") + .build("bootJar"); + File file = new File(this.gradleBuild.getProjectDir(), + "build/libs/" + this.gradleBuild.getProjectDir().getName() + ".jar"); + assertThat(file).isFile(); + assertThat(FileCopyUtils.copyToString(new FileReader(file))).startsWith("custom"); + } + + @Test + public void bootWarPropertiesLauncher() throws IOException { + this.gradleBuild + .script("src/main/gradle/packaging/boot-war-properties-launcher.gradle") + .build("bootWar"); + File file = new File(this.gradleBuild.getProjectDir(), + "build/libs/" + this.gradleBuild.getProjectDir().getName() + ".war"); + assertThat(file).isFile(); + try (JarFile jar = new JarFile(file)) { + assertThat(jar.getManifest().getMainAttributes().getValue("Main-Class")) + .isEqualTo("org.springframework.boot.loader.PropertiesLauncher"); + } + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/PublishingDocumentationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/PublishingDocumentationTests.java new file mode 100644 index 00000000000..b57a0ab60e7 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/PublishingDocumentationTests.java @@ -0,0 +1,54 @@ +/* + * Copyright 2012-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.boot.gradle.docs; + +import java.io.IOException; + +import org.junit.Rule; +import org.junit.Test; + +import org.springframework.boot.gradle.testkit.GradleBuild; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for the publishing documentation. + * + * @author Andy Wilkinson + */ +public class PublishingDocumentationTests { + + @Rule + public GradleBuild gradleBuild = new GradleBuild(); + + @Test + public void mavenUpload() throws IOException { + assertThat(this.gradleBuild.script("src/main/gradle/publishing/maven.gradle") + .build("deployerRepository").getOutput()) + .contains("https://repo.example.com"); + } + + @Test + public void mavenPublish() throws IOException { + assertThat( + this.gradleBuild.script("src/main/gradle/publishing/maven-publish.gradle") + .build("publishingConfiguration").getOutput()) + .contains("MavenPublication") + .contains("https://repo.example.com"); + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/RunningDocumentationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/RunningDocumentationTests.java new file mode 100644 index 00000000000..742ed2b9f1b --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/RunningDocumentationTests.java @@ -0,0 +1,60 @@ +/* + * Copyright 2012-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.boot.gradle.docs; + +import java.io.IOException; + +import org.junit.Rule; +import org.junit.Test; + +import org.springframework.boot.gradle.testkit.GradleBuild; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for the integrating with Actuator documentation. + * + * @author Andy Wilkinson + */ +public class RunningDocumentationTests { + + @Rule + public GradleBuild gradleBuild = new GradleBuild(); + + @Test + public void bootRunMain() throws IOException { + assertThat(this.gradleBuild.script("src/main/gradle/running/boot-run-main.gradle") + .build("configuredMainClass").getOutput()) + .contains("com.example.ExampleApplication"); + } + + @Test + public void applicationPluginMainClassName() throws IOException { + assertThat(this.gradleBuild + .script("src/main/gradle/running/application-plugin-main-class-name.gradle") + .build("configuredMainClass").getOutput()) + .contains("com.example.ExampleApplication"); + } + + @Test + public void bootRunSourceResources() throws IOException { + assertThat(this.gradleBuild + .script("src/main/gradle/running/boot-run-source-resources.gradle") + .build("configuredClasspath").getOutput()).contains("src/main/resources"); + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java index cc8b8cb3898..6dfde693bc8 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java @@ -18,10 +18,9 @@ package org.springframework.boot.gradle.testkit; import java.io.File; import java.io.FileReader; +import java.io.FileWriter; import java.io.IOException; import java.net.URL; -import java.nio.file.Files; -import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -41,6 +40,7 @@ import org.xml.sax.InputSource; import org.springframework.asm.ClassVisitor; import org.springframework.boot.loader.tools.LaunchScript; +import org.springframework.util.FileCopyUtils; /** * A {@link TestRule} for running a Gradle build using {@link GradleRunner}. @@ -143,9 +143,10 @@ public class GradleBuild implements TestRule { } public GradleRunner prepareRunner(String... arguments) throws IOException { - Files.copy(new File(this.script).toPath(), - new File(this.projectDir, "build.gradle").toPath(), - StandardCopyOption.REPLACE_EXISTING); + String scriptContent = FileCopyUtils.copyToString(new FileReader(this.script)) + .replace("{version}", getBootVersion()); + FileCopyUtils.copy(scriptContent, + new FileWriter(new File(this.projectDir, "build.gradle"))); GradleRunner gradleRunner = GradleRunner.create().withProjectDir(this.projectDir) .forwardOutput(); List allArguments = new ArrayList();