Relocate projects to spring-boot-project

Move projects to better reflect the way that Spring Boot is released.

The following projects are under `spring-boot-project`:

  - `spring-boot`
  - `spring-boot-autoconfigure`
  - `spring-boot-tools`
  - `spring-boot-starters`
  - `spring-boot-actuator`
  - `spring-boot-actuator-autoconfigure`
  - `spring-boot-test`
  - `spring-boot-test-autoconfigure`
  - `spring-boot-devtools`
  - `spring-boot-cli`
  - `spring-boot-docs`

See gh-9316
This commit is contained in:
Phillip Webb
2017-09-19 14:29:46 -07:00
parent 0419d42b7c
commit 0ba4830b4f
4023 changed files with 0 additions and 0 deletions

View File

@@ -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 <<reacting-to-other-plugins, reacting to other plugins>>.

View File

@@ -0,0 +1,49 @@
= 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/docs/{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 4.0 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::publishing.adoc[]
include::running.adoc[]
include::integrating-with-actuator.adoc[]
include::reacting.adoc[]

View File

@@ -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 destination
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]
----

View File

@@ -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 <<reacting-to-other-plugins-dependency-management,import the
`spring-boot-dependencies` bom>> 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].

View File

@@ -0,0 +1,192 @@
[[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`].
The `assemble` task is automatically configured to depend upon the `bootJar` task so
running `assemble` (or `build`) will also run the `bootJar` task.
[[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`].
The `assemble` task is automatically configured to depend upon the `bootWar` task so
running `assemble` (or `build`) will also run the `bootWar` task.
[[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 preferred 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-and-normal]]
=== Packaging executable and normal archives
By default, when the `bootJar` or `bootWar` tasks are configured, the `jar` or `war`
tasks are disabled. A project can be configured to build both an executable archive
and a normal archive at the same time by enabling the `jar` or `war` task:
[source,groovy,indent=0,subs="verbatim"]
----
include::../gradle/packaging/boot-jar-and-jar.gradle[tags=enable-jar]
----
To avoid the executable archive and the normal archive from being written to the same
location, one or the other should be configured to use a different location. One way to
do so is by configuring a classifier:
[source,groovy,indent=0,subs="verbatim"]
----
include::../gradle/packaging/boot-jar-and-jar.gradle[tags=classifier]
----
[[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 explicitly 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 `false`:
[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]
----

View File

@@ -0,0 +1,41 @@
[[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
To publish your Spring Boot jar or war, add it to the publication using the `artifact`
method on `MavenPublication`. Pass the task that produces that artifact that you wish
to publish to the `artifact` method. For example, to publish the artifact produced by the
default `bootJar` task:
[source,groovy,indent=0,subs="verbatim"]
----
include::../gradle/publishing/maven-publish.gradle[tags=publishing]
----
[[publishing-your-application-distribution]]
=== 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.

View File

@@ -0,0 +1,79 @@
[[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. Configures the `assemble` task to depend on the `bootJar` task.
3. Disables the `jar` task.
4. Creates a {boot-run-javadoc}[`BootRun`] task named `bootRun` that can be used to run
your application.
5. Creates a configuration named `bootArchives` that contains the artifact produced by
the `bootJar` task.
6. Configures any `JavaCompile` tasks with no configured encoding to use `UTF-8`.
7. Configures any `JavaCompile` tasks to use the `-parameters` compiler argument.
[[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. Configures the `assemble` task to depend on the `bootWar` task.
3. Disables the `war` task.
4. 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.

View File

@@ -0,0 +1,53 @@
[[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 automatically configured to use the runtime classpath of the
main source set. By default, the main class will be discovered 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 explicitly 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]
----
Two properties, `args` and `jvmArgs`, are also provided for configuring the
arguments and JVM arguments that are used to run the application.
For more advanced configuration the `JavaExecSpec` that is used can be customized:
[source,groovy,indent=0,subs="verbatim"]
----
include::../gradle/running/boot-run-custom-exec-spec.gradle[tags=customization]
----
[[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.

View File

@@ -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'

View File

@@ -0,0 +1,3 @@
plugins {
id 'org.springframework.boot' version '{version}'
}

View File

@@ -0,0 +1,11 @@
buildscript {
repositories {
maven { url 'https://repo.spring.io/libs-snapshot' }
}
dependencies {
classpath files(pluginClasspath.split(','))
}
}
apply plugin: 'org.springframework.boot'

View File

@@ -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)
}
}

View File

@@ -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[]

View File

@@ -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[]

View File

@@ -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[]

View File

@@ -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']
}
}

View File

@@ -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[]

View File

@@ -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[]

View File

@@ -0,0 +1,24 @@
buildscript {
dependencies {
classpath files(pluginClasspath.split(','))
}
}
apply plugin: 'org.springframework.boot'
apply plugin: 'java'
// tag::enable-jar[]
jar {
enabled = true
}
// end::enable-jar[]
// tag::classifier[]
bootJar {
classifier = 'boot'
}
// end::classifier[]
bootJar {
mainClass = 'com.example.Application'
}

View File

@@ -0,0 +1,20 @@
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 {
script = file('src/custom.script')
}
}
// end::custom-launch-script[]

View File

@@ -0,0 +1,18 @@
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()
}
// end::include-launch-script[]

View File

@@ -0,0 +1,20 @@
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 {
properties 'logFilename': 'example-app.log'
}
}
// end::launch-script-properties[]

View File

@@ -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[]

View File

@@ -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[]

View File

@@ -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[]

View File

@@ -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[]

View File

@@ -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[]

View File

@@ -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[]

View File

@@ -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) {
artifact bootJar
}
}
repositories {
maven {
url 'https://repo.example.com'
}
}
}
// end::publishing[]
task publishingConfiguration {
doLast {
println publishing.publications.bootJava
println publishing.repositories.maven.url
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -0,0 +1,16 @@
buildscript {
dependencies {
classpath files(pluginClasspath.split(','))
}
}
apply plugin: 'org.springframework.boot'
apply plugin: 'java'
// tag::customization[]
bootRun {
execSpec {
systemProperty 'com.example.foo', 'bar'
}
}
// end::customization[]

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -0,0 +1,117 @@
/*
* 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.dsl;
import java.io.File;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.plugins.BasePlugin;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.tasks.SourceSet;
import org.gradle.jvm.tasks.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.
*
* @author Andy Wilkinson
* @since 2.0.0
*/
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
* Java plugin's {@code classes} task to depend upon it.
* <p>
* 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);
}
/**
* Creates a new {@link BuildInfo} task named {@code bootBuildInfo} and configures the
* Java plugin's {@code classes} task to depend upon it. The task is passed to the
* given {@code configurer} for further configuration.
* <p>
* 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<BuildInfo> configurer) {
BuildInfo bootBuildInfo = this.project.getTasks().create("bootBuildInfo",
BuildInfo.class);
bootBuildInfo.setGroup(BasePlugin.BUILD_GROUP);
bootBuildInfo.setDescription("Generates a META-INF/build-info.properties file.");
this.project.getPlugins().withType(JavaPlugin.class, (plugin) -> {
this.project.getTasks().getByName(JavaPlugin.CLASSES_TASK_NAME)
.dependsOn(bootBuildInfo);
this.project.afterEvaluate((evaluated) -> {
BuildInfoProperties properties = bootBuildInfo.getProperties();
if (properties.getArtifact() == null) {
properties.setArtifact(determineArtifactBaseName());
}
});
bootBuildInfo.setDestinationDir(this.project
.provider(() -> 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");
}
}

View File

@@ -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;

View File

@@ -0,0 +1,101 @@
/*
* 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.IOException;
import java.io.InputStreamReader;
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;
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.tasks.application.CreateBootStartScripts;
/**
* Action that is executed in response to the {@link ApplicationPlugin} being applied.
*
* @author Andy Wilkinson
*/
final class ApplicationPluginAction implements PluginApplicationAction {
@Override
public void execute(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);
bootStartScripts.setDescription("Generates OS-specific start scripts to run the"
+ " project as a Spring Boot application.");
((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<FileCollection>) () -> configuration
.getArtifacts().getFiles()));
bootStartScripts.setClasspath(configuration.getArtifacts().getFiles());
}
});
bootStartScripts.setOutputDir(
project.provider(() -> new File(project.getBuildDir(), "bootScripts")));
bootStartScripts.setApplicationName(
project.provider(() -> applicationConvention.getApplicationName()));
CopySpec binCopySpec = project.copySpec().into("bin").from(bootStartScripts);
binCopySpec.setFileMode(0x755);
distribution.getContents().with(binCopySpec);
}
@Override
public Class<? extends Plugin<Project>> getPluginClass() {
return ApplicationPlugin.class;
}
private String loadResource(String name) {
try (InputStreamReader reader = new InputStreamReader(
getClass().getResourceAsStream(name))) {
char[] buffer = new char[4096];
int read = 0;
StringWriter writer = new StringWriter();
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);
}
}
}

View File

@@ -0,0 +1,85 @@
/*
* 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.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import io.spring.gradle.dependencymanagement.DependencyManagementPlugin;
import io.spring.gradle.dependencymanagement.dsl.DependencyManagementExtension;
import org.gradle.api.Action;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
/**
* {@link Action} that is performed in response to the {@link DependencyManagementPlugin}
* being applied.
*
* @author Andy Wilkinson
*/
final class DependencyManagementPluginAction implements PluginApplicationAction {
private static final String SPRING_BOOT_VERSION = determineSpringBootVersion();
private static final String SPRING_BOOT_BOM = "org.springframework.boot:spring-boot-dependencies:"
+ SPRING_BOOT_VERSION;
@Override
public void execute(Project project) {
project.getExtensions().findByType(DependencyManagementExtension.class)
.imports((importsHandler) -> importsHandler.mavenBom(SPRING_BOOT_BOM));
}
@Override
public Class<? extends Plugin<Project>> getPluginClass() {
return DependencyManagementPlugin.class;
}
private static String determineSpringBootVersion() {
String implementationVersion = DependencyManagementPluginAction.class.getPackage()
.getImplementationVersion();
if (implementationVersion != null) {
return implementationVersion;
}
URL codeSourceLocation = DependencyManagementPluginAction.class
.getProtectionDomain().getCodeSource().getLocation();
try {
URLConnection connection = codeSourceLocation.openConnection();
if (connection instanceof JarURLConnection) {
return getImplementationVersion(
((JarURLConnection) connection).getJarFile());
}
try (JarFile jarFile = new JarFile(new File(codeSourceLocation.toURI()))) {
return getImplementationVersion(jarFile);
}
}
catch (Exception ex) {
return null;
}
}
private static String getImplementationVersion(JarFile jarFile) throws IOException {
return jarFile.getManifest().getMainAttributes()
.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
}
}

View File

@@ -0,0 +1,137 @@
/*
* 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.List;
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.ApplicationPlugin;
import org.gradle.api.plugins.BasePlugin;
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 static final String PARAMETERS_COMPILER_ARG = "-parameters";
private final SinglePublishedArtifact singlePublishedArtifact;
JavaPluginAction(SinglePublishedArtifact singlePublishedArtifact) {
this.singlePublishedArtifact = singlePublishedArtifact;
}
@Override
public Class<? extends Plugin<? extends Project>> getPluginClass() {
return JavaPlugin.class;
}
@Override
public void execute(Project project) {
disableJarTask(project);
configureBuildTask(project);
BootJar bootJar = configureBootJarTask(project);
configureArtifactPublication(project, bootJar);
configureBootRunTask(project);
configureUtf8Encoding(project);
configureParametersCompilerArg(project);
}
private void disableJarTask(Project project) {
project.getTasks().getByName(JavaPlugin.JAR_TASK_NAME).setEnabled(false);
}
private void configureBuildTask(Project project) {
project.getTasks().getByName(BasePlugin.ASSEMBLE_TASK_NAME)
.dependsOn(this.singlePublishedArtifact);
}
private BootJar configureBootJarTask(Project project) {
BootJar bootJar = project.getTasks().create(SpringBootPlugin.BOOT_JAR_TASK_NAME,
BootJar.class);
bootJar.setDescription("Assembles an executable jar archive containing the main"
+ " classes and their dependencies.");
bootJar.setGroup(BasePlugin.BUILD_GROUP);
bootJar.classpath((Callable<FileCollection>) () -> {
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);
}
@SuppressWarnings("unchecked")
private void configureBootRunTask(Project project) {
JavaPluginConvention javaConvention = project.getConvention()
.getPlugin(JavaPluginConvention.class);
BootRun run = project.getTasks().create("bootRun", BootRun.class);
run.setDescription("Runs this project as a Spring Boot application.");
run.setGroup(ApplicationPlugin.APPLICATION_GROUP);
run.classpath(javaConvention.getSourceSets()
.findByName(SourceSet.MAIN_SOURCE_SET_NAME).getRuntimeClasspath());
run.setJvmArgs(project.provider(() -> {
if (project.hasProperty("applicationDefaultJvmArgs")) {
return (List<String>) project.property("applicationDefaultJvmArgs");
}
return Collections.emptyList();
}));
run.setMain(
project.provider(new MainClassConvention(project, run::getClasspath)));
}
private void configureUtf8Encoding(Project project) {
project.afterEvaluate((evaluated) -> evaluated.getTasks()
.withType(JavaCompile.class, (compile) -> {
if (compile.getOptions().getEncoding() == null) {
compile.getOptions().setEncoding("UTF-8");
}
}));
}
private void configureParametersCompilerArg(Project project) {
project.getTasks().withType(JavaCompile.class, (compile) -> {
List<String> compilerArgs = compile.getOptions().getCompilerArgs();
if (!compilerArgs.contains(PARAMETERS_COMPILER_ARG)) {
compilerArgs.add(PARAMETERS_COMPILER_ARG);
}
});
}
}

View File

@@ -0,0 +1,75 @@
/*
* 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.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;
/**
* A {@link Callable} that provides a convention for the project's main class name.
*
* @author Andy Wilkinson
*/
final class MainClassConvention implements Callable<String> {
private static final String SPRING_BOOT_APPLICATION_CLASS_NAME = "org.springframework.boot.autoconfigure.SpringBootApplication";
private final Project project;
private final Supplier<FileCollection> classpathSupplier;
MainClassConvention(Project project, Supplier<FileCollection> classpathSupplier) {
this.project = project;
this.classpathSupplier = classpathSupplier;
}
@Override
public String call() throws Exception {
if (this.project.hasProperty("mainClassName")) {
Object mainClassName = this.project.property("mainClassName");
if (mainClassName != null) {
return mainClassName.toString();
}
}
return resolveMainClass();
}
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 {
return MainClassFinder.findSingleMainClass(file,
SPRING_BOOT_APPLICATION_CLASS_NAME);
}
catch (IOException ex) {
return null;
}
}
}

View File

@@ -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<? extends Plugin<? extends Project>> 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());
}
}

View File

@@ -0,0 +1,39 @@
/*
* 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;
/**
* An {@link Action} to be executed on a {@link Project} in response to a particular type
* of {@link Plugin} being applied.
*
* @author Andy Wilkinson
*/
interface PluginApplicationAction extends Action<Project> {
/**
* The class of the {@code Plugin} that, when applied, will trigger the execution of
* this action.
*
* @return the plugin class
*/
Class<? extends Plugin<? extends Project>> getPluginClass();
}

View File

@@ -0,0 +1,53 @@
/*
* 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.Buildable;
import org.gradle.api.artifacts.PublishArtifact;
import org.gradle.api.artifacts.PublishArtifactSet;
import org.gradle.api.tasks.TaskDependency;
/**
* 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
*/
final class SinglePublishedArtifact implements Buildable {
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.remove(this.currentArtifact);
this.artifacts.add(candidate);
this.currentArtifact = candidate;
}
}
@Override
public TaskDependency getBuildDependencies() {
return this.currentArtifact.getBuildDependencies();
}
}

View File

@@ -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.plugin;
import java.util.Arrays;
import java.util.List;
import org.gradle.api.GradleException;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.util.GradleVersion;
import org.springframework.boot.gradle.dsl.SpringBootExtension;
import org.springframework.boot.gradle.tasks.bundling.BootJar;
import org.springframework.boot.gradle.tasks.bundling.BootWar;
/**
* Gradle plugin for Spring Boot.
*
* @author Phillip Webb
* @author Dave Syer
* @author Andy Wilkinson
*/
public class SpringBootPlugin implements Plugin<Project> {
/**
* The name of the {@link Configuration} that contains Spring Boot archives.
* @since 2.0.0
*/
public static final String BOOT_ARCHIVES_CONFIGURATION_NAME = "bootArchives";
/**
* 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";
@Override
public void apply(Project project) {
verifyGradleVersion();
createExtension(project);
Configuration bootArchives = createBootArchivesConfiguration(project);
registerPluginActions(project, bootArchives);
unregisterUnresolvedDependenciesAnalyzer(project);
}
private void verifyGradleVersion() {
if (GradleVersion.current().compareTo(GradleVersion.version("4.0")) < 0) {
throw new GradleException("Spring Boot plugin requires Gradle 4.0 or later."
+ " The current version is " + GradleVersion.current());
}
}
private void createExtension(Project project) {
project.getExtensions().create("springBoot", SpringBootExtension.class, project);
}
private Configuration createBootArchivesConfiguration(Project project) {
Configuration bootArchives = project.getConfigurations()
.create(BOOT_ARCHIVES_CONFIGURATION_NAME);
bootArchives.setDescription("Configuration for Spring Boot archive artifacts.");
return bootArchives;
}
private void registerPluginActions(Project project, Configuration bootArchives) {
SinglePublishedArtifact singlePublishedArtifact = new SinglePublishedArtifact(
bootArchives.getArtifacts());
List<PluginApplicationAction> 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));
}
}
private void unregisterUnresolvedDependenciesAnalyzer(Project 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));
}
}

View File

@@ -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<ModuleVersionSelector> dependenciesWithNoVersion = new HashSet<>();
void analyze(Set<UnresolvedDependency> 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().isEmpty();
}
}

View File

@@ -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.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.BasePlugin;
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<? extends Plugin<? extends Project>> getPluginClass() {
return WarPlugin.class;
}
@Override
public void execute(Project project) {
project.getTasks().getByName(WarPlugin.WAR_TASK_NAME).setEnabled(false);
BootWar bootWar = project.getTasks().create(SpringBootPlugin.BOOT_WAR_TASK_NAME,
BootWar.class);
bootWar.setGroup(BasePlugin.BUILD_GROUP);
bootWar.setDescription("Assembles an executable war archive containing webapp"
+ " content, and the main classes and their dependencies.");
bootWar.providedClasspath(providedRuntimeConfiguration(project));
ArchivePublishArtifact artifact = new ArchivePublishArtifact(bootWar);
this.singlePublishedArtifact.addCandidate(artifact);
bootWar.conventionMapping("mainClass",
new MainClassConvention(project, bootWar::getClasspath));
}
private Configuration providedRuntimeConfiguration(Project project) {
return project.getConfigurations()
.getByName(WarPlugin.PROVIDED_RUNTIME_CONFIGURATION_NAME);
}
}

View File

@@ -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;

View File

@@ -0,0 +1,86 @@
/*
* 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.application;
import java.io.File;
import org.gradle.api.provider.PropertyState;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.Optional;
import org.gradle.api.tasks.OutputDirectory;
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 {
private final PropertyState<File> outputDir = getProject().property(File.class);
private final PropertyState<String> applicationName = getProject()
.property(String.class);
@Override
@Optional
public String getMainClassName() {
return super.getMainClassName();
}
@Input
@Override
public String getApplicationName() {
return this.applicationName.getOrNull();
}
@Override
public void setApplicationName(String applicationName) {
this.applicationName.set(applicationName);
}
/**
* Sets the application name to the value from the given
* {@code applicationNameProvider}.
* @param applicationNameProvider the provider of the application name
*/
public void setApplicationName(Provider<String> applicationNameProvider) {
this.applicationName.set(applicationNameProvider);
}
@Override
@OutputDirectory
public File getOutputDir() {
return this.outputDir.getOrNull();
}
@Override
public void setOutputDir(File outputDir) {
this.outputDir.set(outputDir);
}
/**
* Sets the output directory to the value from the given {@code outputDirProvider}.
* @param outputDirProvider the provider of the output directory
*/
public void setOutputDir(Provider<File> outputDirProvider) {
this.outputDir.set(outputDirProvider);
}
}

View File

@@ -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;

View File

@@ -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.tasks.buildinfo;
import java.io.File;
import java.io.IOException;
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.provider.PropertyState;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.OutputDirectory;
import org.gradle.api.tasks.TaskAction;
import org.gradle.api.tasks.TaskExecutionException;
import org.springframework.boot.loader.tools.BuildPropertiesWriter;
import org.springframework.boot.loader.tools.BuildPropertiesWriter.ProjectDetails;
/**
* {@link Task} for generating a {@code build-info.properties} file from a
* {@code Project}.
*
* @author Andy Wilkinson
*/
public class BuildInfo extends ConventionTask {
private final BuildInfoProperties properties = new BuildInfoProperties(getProject());
private PropertyState<File> destinationDir = getProject().property(File.class);
/**
* 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.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);
}
}
/**
* 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.isPresent() ? this.destinationDir.get()
: 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.set(destinationDir);
}
/**
* Sets the directory to which the {@code build-info.properties} file will be written
* to the value from the given {@code destinationDirProvider}.
* @param destinationDirProvider the provider of the destination directory
*/
public void setDestinationDir(Provider<File> destinationDirProvider) {
this.destinationDir.set(destinationDirProvider);
}
/**
* Returns the {@link BuildInfoProperties properties} that will be included in the
* {@code build-info.properties} file.
* @return the properties
*/
@Input
public BuildInfoProperties getProperties() {
return this.properties;
}
/**
* Executes the given {@code action} on the {@link #getProperties()} properties.
* @param action the action
*/
public void properties(Action<BuildInfoProperties> action) {
action.execute(this.properties);
}
private Map<String, String> coerceToStringValues(Map<String, Object> input) {
Map<String, String> output = new HashMap<>();
for (Entry<String, Object> entry : input.entrySet()) {
output.put(entry.getKey(), entry.getValue().toString());
}
return output;
}
}

View File

@@ -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<String, Object> 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<String, Object> 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<String, Object> 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;
}
}

View File

@@ -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;

View File

@@ -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.tasks.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
* @since 2.0.0
*/
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<FileTreeElement> spec);
/**
* Returns the {@link LaunchScriptConfiguration} that will control the script that is
* prepended to the archive.
*
* @return the launch script configuration, or {@code null} if the launch script has
* not been configured.
*/
@Input
@Optional
LaunchScriptConfiguration getLaunchScript();
/**
* Configures the archive to have a prepended launch script.
*/
void launchScript();
/**
* Configures the archive to have a prepended launch script, customizing its
* configuration using the given {@code action}.
*
* @param action the action to apply
*/
void launchScript(Action<LaunchScriptConfiguration> 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);
/**
* 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);
}

View File

@@ -0,0 +1,151 @@
/*
* 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.bundling;
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;
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;
/**
* Support class for implementations of {@link BootArchive}.
*
* @author Andy Wilkinson
*/
class BootArchiveSupport {
private static final Set<String> DEFAULT_LAUNCHER_CLASSES;
static {
Set<String> 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 Function<FileCopyDetails, ZipCompression> compressionResolver;
private final PatternSet exclusions = new PatternSet();
private final String loaderMainClass;
private LaunchScriptConfiguration launchScript;
private boolean excludeDevtools = true;
BootArchiveSupport(String loaderMainClass,
Function<FileCopyDetails, ZipCompression> compressionResolver) {
this.loaderMainClass = loaderMainClass;
this.compressionResolver = compressionResolver;
this.requiresUnpack.include(Specs.satisfyNone());
configureExclusions();
}
void configureManifest(Jar jar, String mainClass) {
Attributes attributes = jar.getManifest().getAttributes();
attributes.putIfAbsent("Main-Class", this.loaderMainClass);
attributes.putIfAbsent("Start-Class", mainClass);
}
CopyAction createCopyAction(Jar jar) {
CopyAction copyAction = new BootZipCopyAction(jar.getArchivePath(),
jar.isPreserveFileTimestamps(), isUsingDefaultLoader(jar),
this.requiresUnpack.getAsSpec(), this.exclusions.getAsExcludeSpec(),
this.launchScript, this.compressionResolver, jar.getMetadataCharset());
if (!jar.isReproducibleFileOrder()) {
return copyAction;
}
return new ReproducibleOrderingCopyAction(copyAction);
}
private boolean isUsingDefaultLoader(Jar jar) {
return DEFAULT_LAUNCHER_CLASSES
.contains(jar.getManifest().getAttributes().get("Main-Class"));
}
LaunchScriptConfiguration getLaunchScript() {
return this.launchScript;
}
void setLaunchScript(LaunchScriptConfiguration launchScript) {
this.launchScript = launchScript;
}
void requiresUnpack(String... patterns) {
this.requiresUnpack.include(patterns);
}
void requiresUnpack(Spec<FileTreeElement> spec) {
this.requiresUnpack.include(spec);
}
boolean isExcludeDevtools() {
return this.excludeDevtools;
}
void setExcludeDevtools(boolean excludeDevtools) {
this.excludeDevtools = excludeDevtools;
configureExclusions();
}
private void configureExclusions() {
Set<String> 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;
private ReproducibleOrderingCopyAction(CopyAction delegate) {
this.delegate = delegate;
}
@Override
public WorkResult execute(CopyActionProcessingStream stream) {
return this.delegate.execute((action) -> {
Map<RelativePath, FileCopyDetailsInternal> detailsByPath = new TreeMap<>();
stream.process((details) -> detailsByPath.put(details.getRelativePath(),
details));
detailsByPath.values().stream().forEach(action::processFile);
});
}
}
}

View File

@@ -0,0 +1,159 @@
/*
* 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.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.FileCopyDetails;
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
* @since 2.0.0
*/
public class BootJar extends Jar implements BootArchive {
private BootArchiveSupport support = new BootArchiveSupport(
"org.springframework.boot.loader.JarLauncher", this::resolveZipCompression);
private FileCollection classpath;
private String mainClass;
/**
* Creates a new {@code BootJar} task.
*/
public BootJar() {
CopySpec bootInf = getRootSpec().addChildBeforeSpec(getMainSpec())
.into("BOOT-INF");
bootInf.into("lib", classpathFiles(File::isFile));
bootInf.into("classes", classpathFiles(File::isDirectory));
}
private Action<CopySpec> classpathFiles(Spec<File> filter) {
return (copySpec) -> copySpec
.from((Callable<Iterable<File>>) () -> this.classpath == null
? Collections.emptyList() : this.classpath.filter(filter));
}
@Override
public void copy() {
this.support.configureManifest(this, getMainClass());
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;
}
@Override
public void requiresUnpack(String... patterns) {
this.support.requiresUnpack(patterns);
}
@Override
public void requiresUnpack(Spec<FileTreeElement> spec) {
this.support.requiresUnpack(spec);
}
@Override
public LaunchScriptConfiguration getLaunchScript() {
return this.support.getLaunchScript();
}
@Override
public void launchScript() {
enableLaunchScriptIfNecessary();
}
@Override
public void launchScript(Action<LaunchScriptConfiguration> action) {
action.execute(enableLaunchScriptIfNecessary());
}
@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);
}
@Override
public boolean isExcludeDevtools() {
return this.support.isExcludeDevtools();
}
@Override
public void setExcludeDevtools(boolean excludeDevtools) {
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.
* <p>
* 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;
}
private LaunchScriptConfiguration enableLaunchScriptIfNecessary() {
LaunchScriptConfiguration launchScript = this.support.getLaunchScript();
if (launchScript == null) {
launchScript = new LaunchScriptConfiguration();
this.support.setLaunchScript(launchScript);
}
return launchScript;
}
}

View File

@@ -0,0 +1,167 @@
/*
* 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.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.FileCopyDetails;
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
* @since 2.0.0
*/
public class BootWar extends War implements BootArchive {
private final BootArchiveSupport support = new BootArchiveSupport(
"org.springframework.boot.loader.WarLauncher", this::resolveZipCompression);
private String mainClass;
private FileCollection providedClasspath;
/**
* Creates a new {@code BootWar} task.
*/
public BootWar() {
getWebInf().into("lib-provided",
(copySpec) -> copySpec
.from((Callable<Iterable<File>>) () -> this.providedClasspath == null
? Collections.emptyList() : this.providedClasspath));
}
@Override
public void copy() {
this.support.configureManifest(this, getMainClass());
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;
}
@Override
public void requiresUnpack(String... patterns) {
this.support.requiresUnpack(patterns);
}
@Override
public void requiresUnpack(Spec<FileTreeElement> spec) {
this.support.requiresUnpack(spec);
}
@Override
public LaunchScriptConfiguration getLaunchScript() {
return this.support.getLaunchScript();
}
@Override
public void launchScript() {
enableLaunchScriptIfNecessary();
}
@Override
public void launchScript(Action<LaunchScriptConfiguration> action) {
action.execute(enableLaunchScriptIfNecessary());
}
/**
* 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 {@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
*/
public void providedClasspath(Object... classpath) {
FileCollection existingClasspath = this.providedClasspath;
this.providedClasspath = getProject().files(
existingClasspath == null ? Collections.emptyList() : existingClasspath,
classpath);
}
@Override
public boolean isExcludeDevtools() {
return this.support.isExcludeDevtools();
}
@Override
public void setExcludeDevtools(boolean excludeDevtools) {
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.
* <p>
* 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;
}
private LaunchScriptConfiguration enableLaunchScriptIfNecessary() {
LaunchScriptConfiguration launchScript = this.support.getLaunchScript();
if (launchScript == null) {
launchScript = new LaunchScriptConfiguration();
this.support.setLaunchScript(launchScript);
}
return launchScript;
}
}

View File

@@ -0,0 +1,316 @@
/*
* 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.bundling;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Function;
import java.util.zip.CRC32;
import java.util.zip.ZipInputStream;
import org.apache.commons.compress.archivers.zip.UnixStat;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
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;
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.specs.Specs;
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;
/**
* 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 boolean preserveFileTimestamps;
private final boolean includeDefaultLoader;
private final Spec<FileTreeElement> requiresUnpack;
private final Spec<FileTreeElement> exclusions;
private final LaunchScriptConfiguration launchScript;
private final Function<FileCopyDetails, ZipCompression> compressionResolver;
private final String encoding;
BootZipCopyAction(File output, boolean preserveFileTimestamps,
boolean includeDefaultLoader, Spec<FileTreeElement> requiresUnpack,
Spec<FileTreeElement> exclusions, LaunchScriptConfiguration launchScript,
Function<FileCopyDetails, ZipCompression> compressionResolver,
String encoding) {
this.output = output;
this.preserveFileTimestamps = preserveFileTimestamps;
this.includeDefaultLoader = includeDefaultLoader;
this.requiresUnpack = requiresUnpack;
this.exclusions = exclusions;
this.launchScript = launchScript;
this.compressionResolver = compressionResolver;
this.encoding = encoding;
}
@Override
public WorkResult execute(CopyActionProcessingStream stream) {
ZipArchiveOutputStream zipStream;
Spec<FileTreeElement> loaderEntries;
try {
FileOutputStream fileStream = new FileOutputStream(this.output);
writeLaunchScriptIfNecessary(fileStream);
zipStream = new ZipArchiveOutputStream(fileStream);
if (this.encoding != null) {
zipStream.setEncoding(this.encoding);
}
loaderEntries = writeLoaderClassesIfNecessary(zipStream);
}
catch (IOException ex) {
throw new GradleException("Failed to create " + this.output, ex);
}
try {
stream.process(new ZipStreamAction(zipStream, this.output,
this.preserveFileTimestamps, this.requiresUnpack,
createExclusionSpec(loaderEntries), this.compressionResolver));
}
finally {
try {
zipStream.close();
}
catch (IOException ex) {
// Continue
}
}
return () -> true;
}
@SuppressWarnings("unchecked")
private Spec<FileTreeElement> createExclusionSpec(
Spec<FileTreeElement> loaderEntries) {
return Specs.union(loaderEntries, this.exclusions);
}
private Spec<FileTreeElement> writeLoaderClassesIfNecessary(
ZipArchiveOutputStream out) {
if (!this.includeDefaultLoader) {
return Specs.satisfyNone();
}
return writeLoaderClasses(out);
}
private Spec<FileTreeElement> writeLoaderClasses(ZipArchiveOutputStream out) {
try (ZipInputStream in = new ZipInputStream(getClass()
.getResourceAsStream("/META-INF/loader/spring-boot-loader.jar"))) {
Set<String> entries = new HashSet<>();
java.util.zip.ZipEntry entry;
while ((entry = in.getNextEntry()) != null) {
if (entry.isDirectory() && !entry.getName().startsWith("META-INF/")) {
writeDirectory(new ZipArchiveEntry(entry), out);
entries.add(entry.getName());
}
else if (entry.getName().endsWith(".class")) {
writeClass(new ZipArchiveEntry(entry), in, out);
}
}
return (element) -> {
String path = element.getRelativePath().getPathString();
if (element.isDirectory() && !path.endsWith(("/"))) {
path += "/";
}
return entries.contains(path);
};
}
catch (IOException ex) {
throw new GradleException("Failed to write loader classes", ex);
}
}
private void writeDirectory(ZipArchiveEntry entry, ZipArchiveOutputStream out)
throws IOException {
if (!this.preserveFileTimestamps) {
entry.setTime(GUtil.CONSTANT_TIME_FOR_ZIP_ENTRIES);
}
entry.setUnixMode(UnixStat.DIR_FLAG | UnixStat.DEFAULT_DIR_PERM);
out.putArchiveEntry(entry);
out.closeArchiveEntry();
}
private void writeClass(ZipArchiveEntry entry, ZipInputStream in,
ZipArchiveOutputStream out) throws IOException {
if (!this.preserveFileTimestamps) {
entry.setTime(GUtil.CONSTANT_TIME_FOR_ZIP_ENTRIES);
}
entry.setUnixMode(UnixStat.FILE_FLAG | UnixStat.DEFAULT_FILE_PERM);
out.putArchiveEntry(entry);
byte[] buffer = new byte[4096];
int read;
while ((read = in.read(buffer)) > 0) {
out.write(buffer, 0, read);
}
out.closeArchiveEntry();
}
private void writeLaunchScriptIfNecessary(FileOutputStream fileStream) {
try {
if (this.launchScript != null) {
fileStream.write(new DefaultLaunchScript(this.launchScript.getScript(),
this.launchScript.getProperties()).toByteArray());
this.output.setExecutable(true);
}
}
catch (IOException ex) {
throw new GradleException("Failed to write launch script to " + this.output,
ex);
}
}
private static final class ZipStreamAction
implements CopyActionProcessingStreamAction {
private final ZipArchiveOutputStream zipStream;
private final File output;
private final boolean preserveFileTimestamps;
private final Spec<FileTreeElement> requiresUnpack;
private final Spec<FileTreeElement> exclusions;
private final Function<FileCopyDetails, ZipCompression> compressionType;
private ZipStreamAction(ZipArchiveOutputStream zipStream, File output,
boolean preserveFileTimestamps, Spec<FileTreeElement> requiresUnpack,
Spec<FileTreeElement> exclusions,
Function<FileCopyDetails, ZipCompression> compressionType) {
this.zipStream = zipStream;
this.output = output;
this.preserveFileTimestamps = preserveFileTimestamps;
this.requiresUnpack = requiresUnpack;
this.exclusions = exclusions;
this.compressionType = compressionType;
}
@Override
public void processFile(FileCopyDetailsInternal details) {
if (this.exclusions.isSatisfiedBy(details)) {
return;
}
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 {
ZipArchiveEntry archiveEntry = new ZipArchiveEntry(
details.getRelativePath().getPathString() + '/');
archiveEntry.setUnixMode(UnixStat.DIR_FLAG | details.getMode());
archiveEntry.setTime(getTime(details));
this.zipStream.putArchiveEntry(archiveEntry);
this.zipStream.closeArchiveEntry();
}
private void createFile(FileCopyDetailsInternal details) throws IOException {
String relativePath = details.getRelativePath().getPathString();
ZipArchiveEntry archiveEntry = new ZipArchiveEntry(relativePath);
archiveEntry.setUnixMode(UnixStat.FILE_FLAG | details.getMode());
archiveEntry.setTime(getTime(details));
ZipCompression compression = this.compressionType.apply(details);
if (compression == ZipCompression.STORED) {
prepareStoredEntry(details, archiveEntry);
}
this.zipStream.putArchiveEntry(archiveEntry);
details.copyTo(this.zipStream);
this.zipStream.closeArchiveEntry();
}
private void prepareStoredEntry(FileCopyDetailsInternal details,
ZipArchiveEntry archiveEntry) throws IOException {
archiveEntry.setMethod(java.util.zip.ZipEntry.STORED);
archiveEntry.setSize(details.getSize());
archiveEntry.setCompressedSize(details.getSize());
Crc32OutputStream crcStream = new Crc32OutputStream();
details.copyTo(crcStream);
archiveEntry.setCrc(crcStream.getCrc());
if (this.requiresUnpack.isSatisfiedBy(details)) {
archiveEntry
.setComment("UNPACK:" + FileUtils.sha1Hash(details.getFile()));
}
}
private long getTime(FileCopyDetails details) {
return this.preserveFileTimestamps ? details.getLastModified()
: GUtil.CONSTANT_TIME_FOR_ZIP_ENTRIES;
}
}
/**
* An {@code OutputStream} that provides a CRC-32 of the data that is written to it.
*/
private static final class Crc32OutputStream extends OutputStream {
private final CRC32 crc32 = new CRC32();
@Override
public void write(int b) throws IOException {
this.crc32.update(b);
}
@Override
public void write(byte[] b) throws IOException {
this.crc32.update(b);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
this.crc32.update(b, off, len);
}
private long getCrc() {
return this.crc32.getValue();
}
}
}

View File

@@ -0,0 +1,127 @@
/*
* 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.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
* @since 2.0.0
*/
public class LaunchScriptConfiguration implements Serializable {
private final Map<String, String> properties = new HashMap<>();
private File script;
/**
* Returns the properties that are applied to the launch script when it's being
* including in the executable archive.
*
* @return the properties
*/
public Map<String, String> 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<String, String> 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;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
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.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;
}
}
}

View File

@@ -0,0 +1,39 @@
/*
* 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.bundling;
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 {
/**
* The entry should be {@link ZipEntry#STORED} in the archive.
*/
STORED,
/**
* The entry should be {@link ZipEntry#DEFLATED} in the archive.
*/
DEFLATED
}

View File

@@ -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;

View File

@@ -0,0 +1,190 @@
/*
* 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.run;
import java.util.ArrayList;
import java.util.List;
import org.gradle.api.Action;
import org.gradle.api.DefaultTask;
import org.gradle.api.file.FileCollection;
import org.gradle.api.file.SourceDirectorySet;
import org.gradle.api.provider.PropertyState;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.JavaExec;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.SourceSetOutput;
import org.gradle.api.tasks.TaskAction;
import org.gradle.process.JavaExecSpec;
/**
* Custom {@link JavaExec} task for running a Spring Boot application.
*
* @author Andy Wilkinson
* @since 2.0.0
*/
public class BootRun extends DefaultTask {
private final PropertyState<String> main = getProject().property(String.class);
@SuppressWarnings("unchecked")
private final PropertyState<List<String>> jvmArgs = (PropertyState<List<String>>) (Object) getProject()
.property(List.class);
@SuppressWarnings("unchecked")
private final PropertyState<List<String>> args = (PropertyState<List<String>>) (Object) getProject()
.property(List.class);
private FileCollection classpath = getProject().files();
private List<Action<JavaExecSpec>> execSpecConfigurers = new ArrayList<>();
/**
* Adds the given {@code entries} to the classpath used to run the application.
* @param entries the classpath entries
*/
public void classpath(Object... entries) {
this.classpath = this.classpath.plus(getProject().files(entries));
}
@InputFiles
public FileCollection getClasspath() {
return this.classpath;
}
/**
* 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) {
this.classpath = getProject()
.files(sourceSet.getResources().getSrcDirs(), this.classpath)
.filter((file) -> !file.equals(sourceSet.getOutput().getResourcesDir()));
}
/**
* Returns the name of the main class to be run.
* @return the main class name or {@code null}
*/
public String getMain() {
return this.main.getOrNull();
}
/**
* Sets the main class to be executed using the given {@code mainProvider}.
*
* @param mainProvider provider of the main class name
*/
public void setMain(Provider<String> mainProvider) {
this.main.set(mainProvider);
}
/**
* Sets the main class to be run.
*
* @param main the main class name
*/
public void setMain(String main) {
this.main.set(main);
}
/**
* Returns the JVM arguments to be used to run the application.
* @return the JVM arguments or {@code null}
*/
public List<String> getJvmArgs() {
return this.jvmArgs.getOrNull();
}
/**
* Configures the application to be run using the JVM args provided by the given
* {@code jvmArgsProvider}.
*
* @param jvmArgsProvider the provider of the JVM args
*/
public void setJvmArgs(Provider<List<String>> jvmArgsProvider) {
this.jvmArgs.set(jvmArgsProvider);
}
/**
* Configures the application to be run using the given {@code jvmArgs}.
* @param jvmArgs the JVM args
*/
public void setJvmArgs(List<String> jvmArgs) {
this.jvmArgs.set(jvmArgs);
}
/**
* Returns the arguments to be used to run the application.
* @return the arguments or {@code null}
*/
public List<String> getArgs() {
return this.args.getOrNull();
}
/**
* Configures the application to be run using the given {@code args}.
* @param args the args
*/
public void setArgs(List<String> args) {
this.args.set(args);
}
/**
* Configures the application to be run using the args provided by the given
* {@code argsProvider}.
* @param argsProvider the provider of the args
*/
public void setArgs(Provider<List<String>> argsProvider) {
this.args.set(argsProvider);
}
/**
* Registers the given {@code execSpecConfigurer} to be called to customize the
* {@link JavaExecSpec} prior to running the application.
* @param execSpecConfigurer the configurer
*/
public void execSpec(Action<JavaExecSpec> execSpecConfigurer) {
this.execSpecConfigurers.add(execSpecConfigurer);
}
@TaskAction
public void run() {
getProject().javaexec((spec) -> {
spec.classpath(this.classpath);
spec.setMain(this.main.getOrNull());
if (this.jvmArgs.isPresent()) {
spec.setJvmArgs(this.jvmArgs.get());
}
if (this.args.isPresent()) {
spec.setArgs(this.args.get());
}
if (System.console() != null) {
// Record that the console is available here for AnsiOutput to detect
// later
spec.environment("spring.output.ansi.console-available", true);
}
this.execSpecConfigurers.forEach((configurer) -> configurer.execute(spec));
});
}
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -0,0 +1 @@
implementation-class=org.springframework.boot.gradle.plugin.SpringBootPlugin

View File

@@ -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" "\$@"

View File

@@ -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

View File

@@ -0,0 +1,56 @@
/*
* 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.io.File;
import java.lang.management.ManagementFactory;
import java.util.Arrays;
/**
* Very basic application used for testing {@code BootRun}.
*
* @author Andy Wilkinson
*/
public class BootRunApplication {
protected BootRunApplication() {
}
public static void main(String[] args) {
dumpClassPath();
dumpArgs(args);
dumpJvmArgs();
}
private static void dumpClassPath() {
int i = 1;
for (String entry : ManagementFactory.getRuntimeMXBean().getClassPath()
.split(File.pathSeparator)) {
System.out.println(i++ + ". " + entry);
}
}
private static void dumpArgs(String[] args) {
System.out.println(Arrays.toString(args));
}
private static void dumpJvmArgs() {
System.out.println(ManagementFactory.getRuntimeMXBean().getInputArguments());
}
}

View File

@@ -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");
}
}

View File

@@ -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);
}
}
}

View File

@@ -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");
}
}

View File

@@ -0,0 +1,189 @@
/*
* 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");
}
}
@Test
public void bootJarAndJar() throws IOException {
this.gradleBuild.script("src/main/gradle/packaging/boot-jar-and-jar.gradle")
.build("assemble");
File jar = new File(this.gradleBuild.getProjectDir(),
"build/libs/" + this.gradleBuild.getProjectDir().getName() + ".jar");
assertThat(jar).isFile();
File bootJar = new File(this.gradleBuild.getProjectDir(),
"build/libs/" + this.gradleBuild.getProjectDir().getName() + "-boot.jar");
assertThat(bootJar).isFile();
}
}

View File

@@ -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");
}
}

View File

@@ -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.docs;
import java.io.File;
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 documentation about running a Spring Boot application.
*
* @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(new File("src/main/resources").getPath());
}
@Test
public void bootRunExecSpecCustomization() throws IOException {
this.gradleBuild
.script("src/main/gradle/running/boot-run-custom-exec-spec.gradle")
.build();
}
}

View File

@@ -0,0 +1,129 @@
/*
* 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.dsl;
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.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.dsl.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);
}
}
}

View File

@@ -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.junit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.gradle.api.Rule;
import org.junit.runner.Runner;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.Suite;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.springframework.boot.gradle.testkit.GradleBuild;
/**
* Custom {@link Suite} that runs tests against multiple version of Gradle. Test classes
* using the suite must have a public {@link GradleBuild} field named {@code gradleBuild}
* and annotated with {@link Rule}.
*
* @author Andy Wilkinson
*/
public final class GradleCompatibilitySuite extends Suite {
private static final List<String> GRADLE_VERSIONS = Arrays.asList("default", "4.1",
"4.2");
public GradleCompatibilitySuite(Class<?> clazz) throws InitializationError {
super(clazz, createRunners(clazz));
}
private static List<Runner> createRunners(Class<?> clazz) throws InitializationError {
List<Runner> runners = new ArrayList<>();
for (String version : GRADLE_VERSIONS) {
runners.add(new GradleCompatibilityClassRunner(clazz, version));
}
return runners;
}
private static final class GradleCompatibilityClassRunner
extends BlockJUnit4ClassRunner {
private final String gradleVersion;
private GradleCompatibilityClassRunner(Class<?> klass, String gradleVersion)
throws InitializationError {
super(klass);
this.gradleVersion = gradleVersion;
}
@Override
protected Object createTest() throws Exception {
Object test = super.createTest();
configureTest(test);
return test;
}
private void configureTest(Object test) throws Exception {
GradleBuild gradleBuild = new GradleBuild();
if (!"default".equals(this.gradleVersion)) {
gradleBuild = gradleBuild.gradleVersion(this.gradleVersion);
}
test.getClass().getField("gradleBuild").set(test, gradleBuild);
}
@Override
protected String getName() {
return "Gradle " + this.gradleVersion;
}
@Override
protected String testName(FrameworkMethod method) {
return method.getName() + " [" + getName() + "]";
}
}
}

View File

@@ -0,0 +1,159 @@
/*
* 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.junit.runner.RunWith;
import org.springframework.boot.gradle.junit.GradleCompatibilitySuite;
import org.springframework.boot.gradle.testkit.GradleBuild;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for {@link ApplicationPluginAction}.
*
* @author Andy Wilkinson
*/
@RunWith(GradleCompatibilitySuite.class)
public class ApplicationPluginActionIntegrationTests {
@Rule
public GradleBuild 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 applyingApplicationPluginCreatesBootStartScriptsTask() {
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<String> zipEntryNames(File distribution) throws IOException {
List<String> entryNames = new ArrayList<>();
try (ZipFile zipFile = new ZipFile(distribution)) {
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
entryNames.add(entries.nextElement().getName());
}
}
return entryNames;
}
private List<String> tarEntryNames(File distribution) throws IOException {
List<String> entryNames = new ArrayList<>();
try (TarArchiveInputStream input = new TarArchiveInputStream(
new FileInputStream(distribution))) {
TarArchiveEntry entry;
while ((entry = input.getNextTarEntry()) != null) {
entryNames.add(entry.getName());
}
}
return entryNames;
}
}

View File

@@ -0,0 +1,80 @@
/*
* 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.IOException;
import org.gradle.testkit.runner.BuildResult;
import org.gradle.testkit.runner.TaskOutcome;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.gradle.junit.GradleCompatibilitySuite;
import org.springframework.boot.gradle.testkit.GradleBuild;
import org.springframework.util.FileSystemUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for the configuration applied by
* {@link DependencyManagementPluginAction}.
*
* @author Andy Wilkinson
*/
@RunWith(GradleCompatibilitySuite.class)
public class DependencyManagementPluginActionIntegrationTests {
@Rule
public GradleBuild 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);
}
@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?");
}
}

View File

@@ -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.plugin;
import java.io.File;
import org.gradle.testkit.runner.BuildResult;
import org.gradle.testkit.runner.TaskOutcome;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.gradle.junit.GradleCompatibilitySuite;
import org.springframework.boot.gradle.testkit.GradleBuild;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for {@link WarPluginAction}.
*
* @author Andy Wilkinson
*/
@RunWith(GradleCompatibilitySuite.class)
public class JavaPluginActionIntegrationTests {
@Rule
public GradleBuild 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 javaCompileTasksUseUtf8Encoding() {
assertThat(this.gradleBuild.build("javaCompileEncoding", "-PapplyJavaPlugin")
.getOutput()).contains("compileJava = UTF-8")
.contains("compileTestJava = UTF-8");
}
@Test
public void javaCompileTasksUseParametersCompilerFlagByDefault() {
assertThat(this.gradleBuild.build("javaCompileTasksCompilerArgs").getOutput())
.contains("compileJava compiler args: [-parameters]")
.contains("compileTestJava compiler args: [-parameters]");
}
@Test
public void javaCompileTasksUseParametersAndAdditionalCompilerFlags() {
assertThat(this.gradleBuild.build("javaCompileTasksCompilerArgs").getOutput())
.contains("compileJava compiler args: [-parameters, -Xlint:all]")
.contains("compileTestJava compiler args: [-parameters, -Xlint:all]");
}
@Test
public void javaCompileTasksCanOverrideDefaultParametersCompilerFlag() {
assertThat(this.gradleBuild.build("javaCompileTasksCompilerArgs").getOutput())
.contains("compileJava compiler args: [-Xlint:all]")
.contains("compileTestJava compiler args: [-Xlint:all]");
}
@Test
public void assembleRunsBootJarAndJarIsSkipped() {
BuildResult result = this.gradleBuild.build("assemble");
assertThat(result.task(":bootJar").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
assertThat(result.task(":jar").getOutcome()).isEqualTo(TaskOutcome.SKIPPED);
}
@Test
public void jarAndBootJarCanBothBeBuilt() {
BuildResult result = this.gradleBuild.build("assemble");
assertThat(result.task(":bootJar").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
assertThat(result.task(":jar").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
File buildLibs = new File(this.gradleBuild.getProjectDir(), "build/libs");
assertThat(buildLibs.listFiles()).containsExactlyInAnyOrder(
new File(buildLibs, this.gradleBuild.getProjectDir().getName() + ".jar"),
new File(buildLibs,
this.gradleBuild.getProjectDir().getName() + "-boot.jar"));
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.junit.runner.RunWith;
import org.springframework.boot.gradle.junit.GradleCompatibilitySuite;
import org.springframework.boot.gradle.testkit.GradleBuild;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for {@link MavenPluginAction}.
*
* @author Andy Wilkinson
*/
@RunWith(GradleCompatibilitySuite.class)
public class MavenPluginActionIntegrationTests {
@Rule
public GradleBuild gradleBuild;
@Test
public void clearsConf2ScopeMappingsOfUploadBootArchivesTask() {
assertThat(this.gradleBuild.build("conf2ScopeMappings").getOutput())
.contains("Conf2ScopeMappings = 0");
}
}

View File

@@ -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.plugin;
import org.gradle.testkit.runner.BuildResult;
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 SpringBootPlugin}.
*
* @author Andy Wilkinson
*/
public class SpringBootPluginIntegrationTests {
@Rule
public final GradleBuild gradleBuild = new GradleBuild();
@Test
public void failFastWithVersionOfGradleLowerThanRequired() {
BuildResult result = this.gradleBuild.gradleVersion("3.5.1").buildAndFail();
assertThat(result.getOutput()).contains("Spring Boot plugin requires Gradle 4.0"
+ " or later. The current version is Gradle 3.5.1");
}
@Test
public void succeedWithVersionOfGradleHigherThanRequired() {
this.gradleBuild.gradleVersion("4.0.1").build();
}
@Test
public void succeedWithVersionOfGradleMatchingWhatIsRequired() {
this.gradleBuild.gradleVersion("4.0").build();
}
}

View File

@@ -0,0 +1,75 @@
/*
* 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 org.gradle.testkit.runner.BuildResult;
import org.gradle.testkit.runner.TaskOutcome;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.gradle.junit.GradleCompatibilitySuite;
import org.springframework.boot.gradle.testkit.GradleBuild;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for {@link JavaPluginAction}.
*
* @author Andy Wilkinson
*/
@RunWith(GradleCompatibilitySuite.class)
public class WarPluginActionIntegrationTests {
@Rule
public GradleBuild 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 assembleRunsBootWarAndWarIsSkipped() {
BuildResult result = this.gradleBuild.build("assemble");
assertThat(result.task(":bootWar").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
assertThat(result.task(":war").getOutcome()).isEqualTo(TaskOutcome.SKIPPED);
}
@Test
public void warAndBootWarCanBothBeBuilt() {
BuildResult result = this.gradleBuild.build("assemble");
assertThat(result.task(":bootWar").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
assertThat(result.task(":war").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
File buildLibs = new File(this.gradleBuild.getProjectDir(), "build/libs");
assertThat(buildLibs.listFiles()).containsExactlyInAnyOrder(
new File(buildLibs, this.gradleBuild.getProjectDir().getName() + ".war"),
new File(buildLibs,
this.gradleBuild.getProjectDir().getName() + "-boot.war"));
}
}

View File

@@ -0,0 +1,130 @@
/*
* 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.testkit.runner.TaskOutcome;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.gradle.junit.GradleCompatibilitySuite;
import org.springframework.boot.gradle.testkit.GradleBuild;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for the {@link BuildInfo} task.
*
* @author Andy Wilkinson
*/
@RunWith(GradleCompatibilitySuite.class)
public class BuildInfoIntegrationTests {
@Rule
public GradleBuild 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
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("buildInfoArtifact");
}
@Test
public void notUpToDateWhenProjectGroupChanges() {
notUpToDateWithChangeToProperty("buildInfoGroup");
}
@Test
public void notUpToDateWhenProjectVersionChanges() {
notUpToDateWithChangeToProperty("buildInfoVersion");
}
@Test
public void notUpToDateWhenProjectNameChanges() {
notUpToDateWithChangeToProperty("buildInfoName");
}
@Test
public void notUpToDateWhenAdditionalPropertyChanges() {
notUpToDateWithChangeToProperty("buildInfoAdditional");
}
private void notUpToDateWithChangeToProperty(String name) {
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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,124 @@
/*
* 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.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;
import org.gradle.testkit.runner.UnexpectedBuildFailure;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.gradle.junit.GradleCompatibilitySuite;
import org.springframework.boot.gradle.testkit.GradleBuild;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for {@link BootJar}.
*
* @author Andy Wilkinson
*/
@RunWith(GradleCompatibilitySuite.class)
public abstract class AbstractBootArchiveIntegrationTests {
@Rule
public GradleBuild 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);
}
@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");
}
}
@Test
public void duplicatesAreHandledGracefully() throws IOException {
assertThat(this.gradleBuild.build(this.taskName).task(":" + this.taskName)
.getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
}
}

View File

@@ -0,0 +1,350 @@
/*
* 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.bundling;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.PosixFilePermission;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;
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;
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 <T> the type of the concrete BootArchive implementation
* @author Andy Wilkinson
*/
public abstract class AbstractBootArchiveTests<T extends Jar & BootArchive> {
@Rule
public final TemporaryFolder temp = new TemporaryFolder();
private final Class<T> taskClass;
private final String launcherClass;
private final String libPath;
private final String classesPath;
private T task;
protected AbstractBootArchiveTests(Class<T> 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();
assertThat(jarFile.getEntry("org/springframework/boot/loader/")).isNotNull();
}
}
@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();
assertThat(jarFile.getEntry("org/springframework/boot/loader/")).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.launchScript();
this.task.execute();
assertThat(Files.readAllBytes(this.task.getArchivePath().toPath()))
.startsWith(new DefaultLaunchScript(null, null).toByteArray());
try {
Set<PosixFilePermission> permissions = Files
.getPosixFilePermissions(this.task.getArchivePath().toPath());
assertThat(permissions).contains(PosixFilePermission.OWNER_EXECUTE);
}
catch (UnsupportedOperationException ex) {
// Windows, presumably. Continue
}
}
@Test
public void customLaunchScriptCanBePrepended() throws IOException {
this.task.setMainClass("com.example.Main");
File customScript = this.temp.newFile("custom.script");
Files.write(customScript.toPath(), Arrays.asList("custom script"),
StandardOpenOption.CREATE);
this.task.launchScript((configuration) -> configuration.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");
this.task.launchScript((configuration) -> configuration.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");
assertThat(jarFile.getEntry(
"org/springframework/boot/loader/LaunchedURLClassLoader.class"))
.isNull();
}
}
@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");
}
}
@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<JarEntry> 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<String> textFiles = new ArrayList<>();
try (JarFile jarFile = new JarFile(this.task.getArchivePath())) {
Enumeration<JarEntry> 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");
}
@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();
}
}
@Test
public void allEntriesUseUnixPlatformAndUtf8NameEncoding() throws IOException {
this.task.setMainClass("com.example.Main");
this.task.setMetadataCharset("UTF-8");
File classpathFolder = this.temp.newFolder();
File resource = new File(classpathFolder, "some-resource.xml");
resource.getParentFile().mkdirs();
resource.createNewFile();
this.task.classpath(classpathFolder);
this.task.execute();
File archivePath = this.task.getArchivePath();
try (ZipFile zip = new ZipFile(archivePath)) {
Enumeration<ZipArchiveEntry> entries = zip.getEntries();
while (entries.hasMoreElements()) {
ZipArchiveEntry entry = entries.nextElement();
assertThat(entry.getPlatform()).isEqualTo(ZipArchiveEntry.PLATFORM_UNIX);
assertThat(entry.getGeneralPurposeBit().usesUTF8ForNames()).isTrue();
}
}
}
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;
}
}

View File

@@ -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.tasks.bundling;
/**
* Integration tests for {@link BootJar}.
*
* @author Andy Wilkinson
*/
public class BootJarIntegrationTests extends AbstractBootArchiveIntegrationTests {
public BootJarIntegrationTests() {
super("bootJar");
}
}

View File

@@ -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.tasks.bundling;
/**
* Tests for {@link BootJar}.
*
* @author Andy Wilkinson
*/
public class BootJarTests extends AbstractBootArchiveTests<BootJar> {
public BootJarTests() {
super(BootJar.class, "org.springframework.boot.loader.JarLauncher",
"BOOT-INF/lib", "BOOT-INF/classes");
}
}

View File

@@ -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.tasks.bundling;
/**
* Integration tests for {@link BootJar}.
*
* @author Andy Wilkinson
*/
public class BootWarIntegrationTests extends AbstractBootArchiveIntegrationTests {
public BootWarIntegrationTests() {
super("bootWar");
}
}

View File

@@ -0,0 +1,97 @@
/*
* 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.bundling;
import java.io.File;
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<BootWar> {
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();
}
}
@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();
}
}
@Test
public void webappResourcesInDirectoriesThatOverlapWithLoaderCanBePackaged()
throws IOException {
File webappFolder = this.temp.newFolder("src", "main", "webapp");
File orgFolder = new File(webappFolder, "org");
orgFolder.mkdir();
new File(orgFolder, "foo.txt").createNewFile();
getTask().from(webappFolder);
getTask().setMainClass("com.example.Main");
getTask().execute();
assertThat(getTask().getArchivePath().exists());
try (JarFile jarFile = new JarFile(getTask().getArchivePath())) {
assertThat(jarFile.getEntry("org/")).isNotNull();
assertThat(jarFile.getEntry("org/foo.txt")).isNotNull();
}
}
}

View File

@@ -0,0 +1,77 @@
/*
* 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.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.junit.runner.RunWith;
import org.springframework.boot.gradle.junit.GradleCompatibilitySuite;
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
*/
@RunWith(GradleCompatibilitySuite.class)
public class MavenIntegrationTests {
@Rule
public GradleBuild 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();
}
}

View File

@@ -0,0 +1,76 @@
/*
* 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.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.junit.runner.RunWith;
import org.springframework.boot.gradle.junit.GradleCompatibilitySuite;
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
*/
@RunWith(GradleCompatibilitySuite.class)
public class MavenPublishingIntegrationTests {
@Rule
public GradleBuild 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();
}
}

View File

@@ -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.tasks.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<File> {
private Set<String> expectedContents;
private Set<String> notExpectedContents;
PomCondition() {
this(new HashSet<>(), new HashSet<>());
}
private PomCondition(Set<String> expectedContents, Set<String> 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("<groupId>%s</groupId>", groupId));
return this;
}
PomCondition artifactId(String artifactId) {
this.expectedContents
.add(String.format("<artifactId>%s</artifactId>", artifactId));
return this;
}
PomCondition version(String version) {
this.expectedContents.add(String.format("<version>%s</version>", version));
return this;
}
PomCondition packaging(String packaging) {
this.expectedContents.add(String.format("<packaging>%s</packaging>", packaging));
return this;
}
PomCondition noDependencies() {
this.notExpectedContents.add("<dependencies>");
return this;
}
PomCondition noPackaging() {
this.notExpectedContents.add("<packaging>");
return this;
}
}

View File

@@ -0,0 +1,138 @@
/*
* 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.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.junit.runner.RunWith;
import org.springframework.boot.gradle.junit.GradleCompatibilitySuite;
import org.springframework.boot.gradle.testkit.GradleBuild;
import org.springframework.util.FileSystemUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for the {@link BootRun} task.
*
* @author Andy Wilkinson
*/
@RunWith(GradleCompatibilitySuite.class)
public class BootRunIntegrationTests {
@Rule
public GradleBuild gradleBuild;
@Test
public void basicExecution() throws IOException {
copyApplication();
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. " + canonicalPathOf("build/classes/java/main"));
assertThat(result.getOutput())
.contains("2. " + canonicalPathOf("build/resources/main"));
assertThat(result.getOutput())
.doesNotContain(canonicalPathOf("src/main/resources"));
}
@Test
public void argsCanBeConfigured() throws IOException {
copyApplication();
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("--com.example.foo=bar");
}
@Test
public void jvmArgsCanBeConfigured() throws IOException {
copyApplication();
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("-Dcom.example.foo=bar");
}
@Test
public void execSpecCanBeConfigured() throws IOException {
copyApplication();
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("-Dcom.example.foo=bar");
}
@Test
public void sourceResourcesCanBeUsed() throws IOException {
copyApplication();
BuildResult result = this.gradleBuild.build("bootRun");
assertThat(result.task(":bootRun").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
assertThat(result.getOutput())
.contains("1. " + canonicalPathOf("src/main/resources"));
assertThat(result.getOutput())
.contains("2. " + canonicalPathOf("build/classes/java/main"));
assertThat(result.getOutput())
.doesNotContain(canonicalPathOf("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 applicationPluginMainClassNameIsNotUsedWhenItIsNull() throws IOException {
copyApplication();
BuildResult result = this.gradleBuild.build("echoMainClassName");
assertThat(result.task(":echoMainClassName").getOutcome())
.isEqualTo(TaskOutcome.SUCCESS);
assertThat(result.getOutput())
.contains("Main class name = com.example.BootRunApplication");
}
@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 void copyApplication() 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);
}
private String canonicalPathOf(String path) throws IOException {
return new File(this.gradleBuild.getProjectDir(), path).getCanonicalPath();
}
}

View File

@@ -0,0 +1,207 @@
/*
* 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.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URL;
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.apache.commons.compress.archivers.ArchiveEntry;
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.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}.
*
* @author Andy Wilkinson
*/
public class GradleBuild implements TestRule {
private final TemporaryFolder temp = new TemporaryFolder();
private File projectDir;
private String script;
private String gradleVersion;
@Override
public Statement apply(Statement base, Description description) {
URL scriptUrl = findDefaultScript(description);
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 URL findDefaultScript(Description description) {
URL scriptUrl = getScriptForTestMethod(description);
if (scriptUrl != null) {
return scriptUrl;
}
return getScriptForTestClass(description.getTestClass());
}
private URL getScriptForTestMethod(Description description) {
String name = description.getTestClass().getSimpleName() + "-"
+ removeGradleVersion(description.getMethodName()) + ".gradle";
return description.getTestClass().getResource(name);
}
private String removeGradleVersion(String methodName) {
return methodName.replaceAll("\\[Gradle .+\\]", "").trim();
}
private URL getScriptForTestClass(Class<?> testClass) {
return testClass.getResource(testClass.getSimpleName() + ".gradle");
}
private void before() throws IOException {
this.projectDir = this.temp.newFolder();
}
private void after() {
GradleBuild.this.script = null;
}
private String pluginClasspath() {
return absolutePath("bin") + "," + absolutePath("build/classes/java/main") + ","
+ absolutePath("build/resources/main") + ","
+ pathOfJarContaining(LaunchScript.class) + ","
+ pathOfJarContaining(ClassVisitor.class) + ","
+ pathOfJarContaining(DependencyManagementPlugin.class) + ","
+ pathOfJarContaining(ArchiveEntry.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) {
this.script = script;
return this;
}
public BuildResult build(String... arguments) {
try {
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 {
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)
.withDebug(true);
if (this.gradleVersion != null) {
gradleRunner.withGradleVersion(this.gradleVersion);
}
List<String> allArguments = new ArrayList<>();
allArguments.add("-PpluginClasspath=" + pluginClasspath());
allArguments.add("-PbootVersion=" + getBootVersion());
allArguments.add("--stacktrace");
allArguments.addAll(Arrays.asList(arguments));
return gradleRunner.withArguments(allArguments);
}
public File getProjectDir() {
return this.projectDir;
}
public void setProjectDir(File projectDir) {
this.projectDir = projectDir;
}
public GradleBuild gradleVersion(String version) {
this.gradleVersion = version;
return this;
}
public String getGradleVersion() {
return this.gradleVersion;
}
private 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);
}
}
}

View File

@@ -0,0 +1,21 @@
buildscript {
dependencies {
classpath files(pluginClasspath.split(','))
}
}
apply plugin: 'org.springframework.boot'
apply plugin: 'java'
group = 'com.example'
version = '1.0'
springBoot {
buildInfo {
properties {
additionalProperties = [
'a': 'alpha', 'b': 'bravo'
]
}
}
}

View File

@@ -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()
}

View File

@@ -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()
}

View File

@@ -0,0 +1,12 @@
buildscript {
dependencies {
classpath files(pluginClasspath.split(','))
}
}
apply plugin: 'org.springframework.boot'
apply plugin: 'java'
springBoot {
buildInfo()
}

View File

@@ -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()
}

View File

@@ -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()
}

Some files were not shown because too many files have changed in this diff Show More