diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchive.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchive.java index 46c8db3cd5..3ab193e493 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchive.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchive.java @@ -99,4 +99,22 @@ public interface BootArchive extends Task { */ void classpath(Object... classpath); + /** + * Returns {@code true} if the Devtools jar should be excluded, otherwise + * {@code false}. + * + * @return {@code true} if the Devtools jar should be excluded, or {@code false} if + * not + */ + @Input + boolean isExcludeDevtools(); + + /** + * Sets whether or not the Devtools jar should be excluded. + * + * @param excludeDevtools {@code true} if the Devtools jar should be excluded, or + * {@code false} if not + */ + void setExcludeDevtools(boolean excludeDevtools); + } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchiveSupport.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchiveSupport.java index 2c40538c0b..99d5ef3fe4 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchiveSupport.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootArchiveSupport.java @@ -45,13 +45,18 @@ class BootArchiveSupport { private final Set storedPathPrefixes; + private final PatternSet exclusions = new PatternSet(); + private String loaderMainClass; private LaunchScriptConfiguration launchScript = new LaunchScriptConfiguration(); + private boolean excludeDevtools = true; + BootArchiveSupport(String... storedPathPrefixes) { this.storedPathPrefixes = new HashSet<>(Arrays.asList(storedPathPrefixes)); this.requiresUnpack.include(Specs.satisfyNone()); + configureExclusions(); } void configureManifest(Jar jar, String mainClass) { @@ -62,18 +67,15 @@ class BootArchiveSupport { CopyAction createCopyAction(Jar jar) { CopyAction copyAction = new BootZipCopyAction(jar.getArchivePath(), - jar.isPreserveFileTimestamps(), this::requiresUnpacking, - this.launchScript, this.storedPathPrefixes); + jar.isPreserveFileTimestamps(), this.requiresUnpack.getAsSpec(), + this.exclusions.getAsExcludeSpec(), this.launchScript, + this.storedPathPrefixes); if (!jar.isReproducibleFileOrder()) { return copyAction; } return new ReproducibleOrderingCopyAction(copyAction); } - private boolean requiresUnpacking(FileTreeElement fileTreeElement) { - return this.requiresUnpack.getAsSpec().isSatisfiedBy(fileTreeElement); - } - String getLoaderMainClass() { return this.loaderMainClass; } @@ -98,6 +100,23 @@ class BootArchiveSupport { this.requiresUnpack.include(spec); } + boolean isExcludeDevtools() { + return this.excludeDevtools; + } + + void setExcludeDevtools(boolean excludeDevtools) { + this.excludeDevtools = excludeDevtools; + configureExclusions(); + } + + private void configureExclusions() { + Set excludes = new HashSet(); + if (this.excludeDevtools) { + excludes.add("**/spring-boot-devtools-*.jar"); + } + this.exclusions.setExcludes(excludes); + } + private static final class ReproducibleOrderingCopyAction implements CopyAction { private final CopyAction delegate; diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootJar.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootJar.java index 7625ff3cf6..f0af0aed7f 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootJar.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootJar.java @@ -112,4 +112,14 @@ public class BootJar extends Jar implements BootArchive { classpath); } + @Override + public boolean isExcludeDevtools() { + return this.support.isExcludeDevtools(); + } + + @Override + public void setExcludeDevtools(boolean excludeDevtools) { + this.support.setExcludeDevtools(excludeDevtools); + } + } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootWar.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootWar.java index 647f42ebf0..59c96290b8 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootWar.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootWar.java @@ -112,4 +112,14 @@ public class BootWar extends War implements BootArchive { classpath); } + @Override + public boolean isExcludeDevtools() { + return this.support.isExcludeDevtools(); + } + + @Override + public void setExcludeDevtools(boolean excludeDevtools) { + this.support.setExcludeDevtools(excludeDevtools); + } + } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootZipCopyAction.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootZipCopyAction.java index b29e224c86..a49efae2b0 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootZipCopyAction.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BootZipCopyAction.java @@ -55,16 +55,19 @@ class BootZipCopyAction implements CopyAction { private final Spec requiresUnpack; + private final Spec exclusions; + private final LaunchScriptConfiguration launchScript; private final Set storedPathPrefixes; BootZipCopyAction(File output, boolean preserveFileTimestamps, - Spec requiresUnpack, LaunchScriptConfiguration launchScript, - Set storedPathPrefixes) { + Spec requiresUnpack, Spec exclusions, + LaunchScriptConfiguration launchScript, Set storedPathPrefixes) { this.output = output; this.preserveFileTimestamps = preserveFileTimestamps; this.requiresUnpack = requiresUnpack; + this.exclusions = exclusions; this.launchScript = launchScript; this.storedPathPrefixes = storedPathPrefixes; } @@ -83,7 +86,7 @@ class BootZipCopyAction implements CopyAction { } try { stream.process(new ZipStreamAction(zipStream, this.output, - this.preserveFileTimestamps, this.requiresUnpack, + this.preserveFileTimestamps, this.requiresUnpack, this.exclusions, this.storedPathPrefixes)); } finally { @@ -148,20 +151,26 @@ class BootZipCopyAction implements CopyAction { private final Spec requiresUnpack; + private final Spec exclusions; + private final Set storedPathPrefixes; private ZipStreamAction(ZipOutputStream zipStream, File output, boolean preserveFileTimestamps, Spec requiresUnpack, - Set storedPathPrefixes) { + Spec exclusions, Set storedPathPrefixes) { this.zipStream = zipStream; this.output = output; this.preserveFileTimestamps = preserveFileTimestamps; this.requiresUnpack = requiresUnpack; + this.exclusions = exclusions; this.storedPathPrefixes = storedPathPrefixes; } @Override public void processFile(FileCopyDetailsInternal details) { + if (this.exclusions.isSatisfiedBy(details)) { + return; + } try { if (details.isDirectory()) { createDirectory(details); diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveTests.java index 6a1c2cc7fd..ce7a164a11 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveTests.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveTests.java @@ -262,6 +262,31 @@ public abstract class AbstractBootArchiveTests { assertThat(textFiles).containsExactly("alpha.txt", "bravo.txt", "charlie.txt"); } + @Test + public void devtoolsJarIsExcludedByDefault() throws IOException { + this.task.setMainClass("com.example.Main"); + this.task.classpath(this.temp.newFile("spring-boot-devtools-0.1.2.jar")); + this.task.execute(); + assertThat(this.task.getArchivePath().exists()); + try (JarFile jarFile = new JarFile(this.task.getArchivePath())) { + assertThat(jarFile.getEntry(this.libPath + "/spring-boot-devtools-0.1.2.jar")) + .isNull(); + } + } + + @Test + public void devtoolsJarCanBeIncluded() throws IOException { + this.task.setMainClass("com.example.Main"); + this.task.classpath(this.temp.newFile("spring-boot-devtools-0.1.2.jar")); + this.task.setExcludeDevtools(false); + this.task.execute(); + assertThat(this.task.getArchivePath().exists()); + try (JarFile jarFile = new JarFile(this.task.getArchivePath())) { + assertThat(jarFile.getEntry(this.libPath + "/spring-boot-devtools-0.1.2.jar")) + .isNotNull(); + } + } + private T configure(T task) throws IOException { AbstractArchiveTask archiveTask = task; archiveTask.setBaseName("test"); diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootWarTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootWarTests.java index 383d6350d1..8278678b16 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootWarTests.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/BootWarTests.java @@ -47,4 +47,33 @@ public class BootWarTests extends AbstractBootArchiveTests { } } + @Test + public void devtoolsJarIsExcludedByDefaultWhenItsOnTheProvidedClasspath() + throws IOException { + getTask().setMainClass("com.example.Main"); + getTask().providedClasspath(this.temp.newFile("spring-boot-devtools-0.1.2.jar")); + getTask().execute(); + assertThat(getTask().getArchivePath().exists()); + try (JarFile jarFile = new JarFile(getTask().getArchivePath())) { + assertThat(jarFile + .getEntry("WEB-INF/lib-provided/spring-boot-devtools-0.1.2.jar")) + .isNull(); + } + } + + @Test + public void devtoolsJarCanBeIncludedWhenItsOnTheProvidedClasspath() + throws IOException { + getTask().setMainClass("com.example.Main"); + getTask().providedClasspath(this.temp.newFile("spring-boot-devtools-0.1.2.jar")); + getTask().setExcludeDevtools(false); + getTask().execute(); + assertThat(getTask().getArchivePath().exists()); + try (JarFile jarFile = new JarFile(getTask().getArchivePath())) { + assertThat(jarFile + .getEntry("WEB-INF/lib-provided/spring-boot-devtools-0.1.2.jar")) + .isNotNull(); + } + } + }