Commit e79ef9b7 authored by Stephane Nicoll's avatar Stephane Nicoll Committed by Phillip Webb

Add option to exclude devtools from fat jar

Add an `excludeDevtools` property to both the Maven and Gradle plugin
that removes `org.springframework.boot:spring-boot-devtools` (if
necessary) when repackaging the application.

Closes gh-3171
parent e2fc30ef
...@@ -395,6 +395,10 @@ want the other Boot features but not this one) ...@@ -395,6 +395,10 @@ want the other Boot features but not this one)
|`embeddedLaunchScriptProperties` |`embeddedLaunchScriptProperties`
|Additional properties that to be expanded in the launch script. The default script |Additional properties that to be expanded in the launch script. The default script
supports a `mode` property which can contain the values `auto`, `service` or `run`. supports a `mode` property which can contain the values `auto`, `service` or `run`.
|`excludeDevtools`
|Boolean flag to indicate if the devtools jar should be excluded from the repackaged
archives. Defaults to `false`.
|=== |===
......
...@@ -844,6 +844,10 @@ applied to other modules using your project. Gradle does not support `optional` ...@@ -844,6 +844,10 @@ applied to other modules using your project. Gradle does not support `optional`
dependencies out-of-the-box so you may want to have a look to the dependencies out-of-the-box so you may want to have a look to the
{propdeps-plugin}[`propdeps-plugin`] in the meantime. {propdeps-plugin}[`propdeps-plugin`] in the meantime.
TIP: If you want to ensure that devtools is never included in a production build, you can
use set the `excludeDevtools` build property to completely remove the JAR. The property is
supported with both the Maven and Gradle plugins.
[[using-boot-devtools-property-defaults]] [[using-boot-devtools-property-defaults]]
......
...@@ -47,19 +47,24 @@ public class RepackagingTests { ...@@ -47,19 +47,24 @@ public class RepackagingTests {
} }
@Test @Test
public void repackagingEnabled() { public void repackagingEnabled() throws IOException {
project.newBuild().forTasks("clean", "build") project.newBuild().forTasks("clean", "build")
.withArguments("-PbootVersion=" + BOOT_VERSION, "-Prepackage=true").run(); .withArguments("-PbootVersion=" + BOOT_VERSION, "-Prepackage=true",
"-PexcludeDevtools=false")
.run();
File buildLibs = new File("target/repackage/build/libs"); File buildLibs = new File("target/repackage/build/libs");
assertTrue(new File(buildLibs, "repackage.jar").exists()); File repackageFile = new File(buildLibs, "repackage.jar");
assertTrue(repackageFile.exists());
assertTrue(new File(buildLibs, "repackage.jar.original").exists()); assertTrue(new File(buildLibs, "repackage.jar.original").exists());
assertFalse(new File(buildLibs, "repackage-sources.jar.original").exists()); assertFalse(new File(buildLibs, "repackage-sources.jar.original").exists());
assertTrue(isDevToolsJarIncluded(repackageFile));
} }
@Test @Test
public void repackagingDisabled() { public void repackagingDisabled() {
project.newBuild().forTasks("clean", "build") project.newBuild().forTasks("clean", "build")
.withArguments("-PbootVersion=" + BOOT_VERSION, "-Prepackage=false") .withArguments("-PbootVersion=" + BOOT_VERSION, "-Prepackage=false",
"-PexcludeDevtools=false")
.run(); .run();
File buildLibs = new File("target/repackage/build/libs"); File buildLibs = new File("target/repackage/build/libs");
assertTrue(new File(buildLibs, "repackage.jar").exists()); assertTrue(new File(buildLibs, "repackage.jar").exists());
...@@ -70,7 +75,8 @@ public class RepackagingTests { ...@@ -70,7 +75,8 @@ public class RepackagingTests {
@Test @Test
public void repackagingDisabledWithCustomRepackagedJar() { public void repackagingDisabledWithCustomRepackagedJar() {
project.newBuild().forTasks("clean", "build", "customRepackagedJar") project.newBuild().forTasks("clean", "build", "customRepackagedJar")
.withArguments("-PbootVersion=" + BOOT_VERSION, "-Prepackage=false") .withArguments("-PbootVersion=" + BOOT_VERSION, "-Prepackage=false",
"-PexcludeDevtools=false")
.run(); .run();
File buildLibs = new File("target/repackage/build/libs"); File buildLibs = new File("target/repackage/build/libs");
assertTrue(new File(buildLibs, "repackage.jar").exists()); assertTrue(new File(buildLibs, "repackage.jar").exists());
...@@ -84,7 +90,8 @@ public class RepackagingTests { ...@@ -84,7 +90,8 @@ public class RepackagingTests {
public void repackagingDisabledWithCustomRepackagedJarUsingStringJarTaskReference() { public void repackagingDisabledWithCustomRepackagedJarUsingStringJarTaskReference() {
project.newBuild() project.newBuild()
.forTasks("clean", "build", "customRepackagedJarWithStringReference") .forTasks("clean", "build", "customRepackagedJarWithStringReference")
.withArguments("-PbootVersion=" + BOOT_VERSION, "-Prepackage=false") .withArguments("-PbootVersion=" + BOOT_VERSION, "-Prepackage=false",
"-PexcludeDevtools=false")
.run(); .run();
File buildLibs = new File("target/repackage/build/libs"); File buildLibs = new File("target/repackage/build/libs");
assertTrue(new File(buildLibs, "repackage.jar").exists()); assertTrue(new File(buildLibs, "repackage.jar").exists());
...@@ -97,7 +104,9 @@ public class RepackagingTests { ...@@ -97,7 +104,9 @@ public class RepackagingTests {
@Test @Test
public void repackagingEnabledWithCustomRepackagedJar() { public void repackagingEnabledWithCustomRepackagedJar() {
project.newBuild().forTasks("clean", "build", "customRepackagedJar") project.newBuild().forTasks("clean", "build", "customRepackagedJar")
.withArguments("-PbootVersion=" + BOOT_VERSION, "-Prepackage=true").run(); .withArguments("-PbootVersion=" + BOOT_VERSION, "-Prepackage=true",
"-PexcludeDevtools=false")
.run();
File buildLibs = new File("target/repackage/build/libs"); File buildLibs = new File("target/repackage/build/libs");
assertTrue(new File(buildLibs, "repackage.jar").exists()); assertTrue(new File(buildLibs, "repackage.jar").exists());
assertTrue(new File(buildLibs, "repackage.jar.original").exists()); assertTrue(new File(buildLibs, "repackage.jar.original").exists());
...@@ -110,7 +119,9 @@ public class RepackagingTests { ...@@ -110,7 +119,9 @@ public class RepackagingTests {
public void repackagingEnableWithCustomRepackagedJarUsingStringJarTaskReference() { public void repackagingEnableWithCustomRepackagedJarUsingStringJarTaskReference() {
project.newBuild() project.newBuild()
.forTasks("clean", "build", "customRepackagedJarWithStringReference") .forTasks("clean", "build", "customRepackagedJarWithStringReference")
.withArguments("-PbootVersion=" + BOOT_VERSION, "-Prepackage=true").run(); .withArguments("-PbootVersion=" + BOOT_VERSION, "-Prepackage=true",
"-PexcludeDevtools=false")
.run();
File buildLibs = new File("target/repackage/build/libs"); File buildLibs = new File("target/repackage/build/libs");
assertTrue(new File(buildLibs, "repackage.jar").exists()); assertTrue(new File(buildLibs, "repackage.jar").exists());
assertTrue(new File(buildLibs, "repackage.jar.original").exists()); assertTrue(new File(buildLibs, "repackage.jar.original").exists());
...@@ -124,10 +135,38 @@ public class RepackagingTests { ...@@ -124,10 +135,38 @@ public class RepackagingTests {
FileCopyUtils.copy(new File("src/test/resources/foo.jar"), FileCopyUtils.copy(new File("src/test/resources/foo.jar"),
new File("target/repackage/foo.jar")); new File("target/repackage/foo.jar"));
project.newBuild().forTasks("clean", "build") project.newBuild().forTasks("clean", "build")
.withArguments("-PbootVersion=" + BOOT_VERSION, "-Prepackage=true").run(); .withArguments("-PbootVersion=" + BOOT_VERSION, "-Prepackage=true",
"-PexcludeDevtools=false")
.run();
File buildLibs = new File("target/repackage/build/libs"); File buildLibs = new File("target/repackage/build/libs");
JarFile jarFile = new JarFile(new File(buildLibs, "repackage.jar")); JarFile jarFile = new JarFile(new File(buildLibs, "repackage.jar"));
assertThat(jarFile.getEntry("lib/foo.jar"), notNullValue()); assertThat(jarFile.getEntry("lib/foo.jar"), notNullValue());
jarFile.close(); jarFile.close();
} }
@Test
public void repackagingEnabledExcludeDevtools() throws IOException {
project.newBuild().forTasks("clean", "build")
.withArguments("-PbootVersion=" + BOOT_VERSION, "-Prepackage=true",
"-PexcludeDevtools=true")
.run();
File buildLibs = new File("target/repackage/build/libs");
File repackageFile = new File(buildLibs, "repackage.jar");
assertTrue(repackageFile.exists());
assertTrue(new File(buildLibs, "repackage.jar.original").exists());
assertFalse(new File(buildLibs, "repackage-sources.jar.original").exists());
assertFalse(isDevToolsJarIncluded(repackageFile));
}
private boolean isDevToolsJarIncluded(File repackageFile) throws IOException {
JarFile jarFile = new JarFile(repackageFile);
try {
String name = "lib/spring-boot-devtools-" + BOOT_VERSION + ".jar";
return jarFile.getEntry(name) != null;
}
finally {
jarFile.close();
}
}
} }
...@@ -18,11 +18,13 @@ apply plugin: 'java' ...@@ -18,11 +18,13 @@ apply plugin: 'java'
dependencies { dependencies {
compile 'org.springframework.boot:spring-boot-starter-freemarker' compile 'org.springframework.boot:spring-boot-starter-freemarker'
compile 'org.springframework.boot:spring-boot-starter-web' compile 'org.springframework.boot:spring-boot-starter-web'
compile 'org.springframework.boot:spring-boot-devtools'
compile files("foo.jar") compile files("foo.jar")
} }
springBoot { springBoot {
mainClass = 'foo.bar.Baz' mainClass = 'foo.bar.Baz'
excludeDevtools = Boolean.valueOf(project.excludeDevtools)
} }
bootRepackage.enabled = Boolean.valueOf(project.repackage) bootRepackage.enabled = Boolean.valueOf(project.repackage)
......
...@@ -38,6 +38,7 @@ import org.springframework.boot.loader.tools.Layouts; ...@@ -38,6 +38,7 @@ import org.springframework.boot.loader.tools.Layouts;
* *
* @author Phillip Webb * @author Phillip Webb
* @author Dave Syer * @author Dave Syer
* @author Stephane Nicoll
*/ */
public class SpringBootPluginExtension { public class SpringBootPluginExtension {
...@@ -87,6 +88,11 @@ public class SpringBootPluginExtension { ...@@ -87,6 +88,11 @@ public class SpringBootPluginExtension {
*/ */
Set<String> requiresUnpack; Set<String> requiresUnpack;
/**
* Whether Spring Boot Devtools should be excluded from the fat jar.
*/
boolean excludeDevtools = false;
/** /**
* Location of an agent jar to attach to the VM when running the application with * Location of an agent jar to attach to the VM when running the application with
* runJar task. * runJar task.
...@@ -186,6 +192,14 @@ public class SpringBootPluginExtension { ...@@ -186,6 +192,14 @@ public class SpringBootPluginExtension {
this.requiresUnpack = requiresUnpack; this.requiresUnpack = requiresUnpack;
} }
public boolean isExcludeDevtools() {
return this.excludeDevtools;
}
public void setExcludeDevtools(boolean excludeDevtools) {
this.excludeDevtools = excludeDevtools;
}
public File getAgent() { public File getAgent() {
return this.agent; return this.agent;
} }
......
...@@ -155,12 +155,26 @@ class ProjectLibraries implements Libraries { ...@@ -155,12 +155,26 @@ class ProjectLibraries implements Libraries {
if (libraries != null) { if (libraries != null) {
Set<String> duplicates = getDuplicates(libraries); Set<String> duplicates = getDuplicates(libraries);
for (GradleLibrary library : libraries) { for (GradleLibrary library : libraries) {
library.setIncludeGroupName(duplicates.contains(library.getName())); if (!isExcluded(library)) {
callback.library(library); library.setIncludeGroupName(duplicates.contains(library.getName()));
callback.library(library);
}
} }
} }
} }
private boolean isExcluded(GradleLibrary library) {
if (this.extension.isExcludeDevtools() && isDevToolsJar(library)) {
return true;
}
return false;
}
private boolean isDevToolsJar(GradleLibrary library) {
return "org.springframework.boot".equals(library.getGroup())
&& library.getName().startsWith("spring-boot-devtools");
}
private Set<String> getDuplicates(Set<GradleLibrary> libraries) { private Set<String> getDuplicates(Set<GradleLibrary> libraries) {
Set<String> duplicates = new HashSet<String>(); Set<String> duplicates = new HashSet<String>();
Set<String> seen = new HashSet<String>(); Set<String> seen = new HashSet<String>();
...@@ -187,6 +201,10 @@ class ProjectLibraries implements Libraries { ...@@ -187,6 +201,10 @@ class ProjectLibraries implements Libraries {
this.includeGroupName = includeGroupName; this.includeGroupName = includeGroupName;
} }
public String getGroup() {
return this.group;
}
@Override @Override
public String getName() { public String getName() {
String name = super.getName(); String name = super.getName();
......
/* /*
* Copyright 2012-2014 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package org.springframework.boot.maven; package org.springframework.boot.maven;
import java.util.Arrays;
import java.util.List; import java.util.List;
import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.Artifact;
...@@ -29,6 +30,10 @@ import org.apache.maven.artifact.Artifact; ...@@ -29,6 +30,10 @@ import org.apache.maven.artifact.Artifact;
*/ */
public class ExcludeFilter extends DependencyFilter { public class ExcludeFilter extends DependencyFilter {
public ExcludeFilter(Exclude... excludes) {
this(Arrays.asList(excludes));
}
public ExcludeFilter(List<Exclude> excludes) { public ExcludeFilter(List<Exclude> excludes) {
super(excludes); super(excludes);
} }
......
...@@ -35,6 +35,7 @@ import org.apache.maven.plugins.annotations.Parameter; ...@@ -35,6 +35,7 @@ import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope; import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject; import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectHelper; import org.apache.maven.project.MavenProjectHelper;
import org.apache.maven.shared.artifact.filter.collection.ArtifactsFilter;
import org.springframework.boot.loader.tools.DefaultLaunchScript; import org.springframework.boot.loader.tools.DefaultLaunchScript;
import org.springframework.boot.loader.tools.LaunchScript; import org.springframework.boot.loader.tools.LaunchScript;
import org.springframework.boot.loader.tools.Layout; import org.springframework.boot.loader.tools.Layout;
...@@ -153,6 +154,13 @@ public class RepackageMojo extends AbstractDependencyFilterMojo { ...@@ -153,6 +154,13 @@ public class RepackageMojo extends AbstractDependencyFilterMojo {
@Parameter @Parameter
private Properties embeddedLaunchScriptProperties; private Properties embeddedLaunchScriptProperties;
/**
* Exclude Spring Boot devtools.
* @since 1.3
*/
@Parameter(defaultValue = "false")
private boolean excludeDevtools;
@Override @Override
public void execute() throws MojoExecutionException, MojoFailureException { public void execute() throws MojoExecutionException, MojoFailureException {
if (this.project.getPackaging().equals("pom")) { if (this.project.getPackaging().equals("pom")) {
...@@ -190,7 +198,7 @@ public class RepackageMojo extends AbstractDependencyFilterMojo { ...@@ -190,7 +198,7 @@ public class RepackageMojo extends AbstractDependencyFilterMojo {
} }
Set<Artifact> artifacts = filterDependencies(this.project.getArtifacts(), Set<Artifact> artifacts = filterDependencies(this.project.getArtifacts(),
getFilters()); getFilters(getAdditionalFilters()));
Libraries libraries = new ArtifactsLibraries(artifacts, this.requiresUnpack, Libraries libraries = new ArtifactsLibraries(artifacts, this.requiresUnpack,
getLog()); getLog());
...@@ -213,6 +221,17 @@ public class RepackageMojo extends AbstractDependencyFilterMojo { ...@@ -213,6 +221,17 @@ public class RepackageMojo extends AbstractDependencyFilterMojo {
} }
} }
private ArtifactsFilter[] getAdditionalFilters() {
if (this.excludeDevtools) {
Exclude exclude = new Exclude();
exclude.setGroupId("org.springframework.boot");
exclude.setArtifactId("spring-boot-devtools");
ExcludeFilter filter = new ExcludeFilter(exclude);
return new ArtifactsFilter[] { filter };
}
return new ArtifactsFilter[] {};
}
private File getTargetFile() { private File getTargetFile() {
String classifier = (this.classifier == null ? "" : this.classifier.trim()); String classifier = (this.classifier == null ? "" : this.classifier.trim());
if (classifier.length() > 0 && !classifier.startsWith("-")) { if (classifier.length() > 0 && !classifier.startsWith("-")) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment