diff --git a/spring-boot-docs/src/main/asciidoc/build-tool-plugins.adoc b/spring-boot-docs/src/main/asciidoc/build-tool-plugins.adoc
index 6b662acd1a..55449e04e6 100644
--- a/spring-boot-docs/src/main/asciidoc/build-tool-plugins.adoc
+++ b/spring-boot-docs/src/main/asciidoc/build-tool-plugins.adoc
@@ -493,7 +493,7 @@ The following configuration options are available:
[[build-tool-plugins-gradle-configuration-layouts]]
-==== Available layouts
+==== Available built-in layouts
The `layout` attribute configures the format of the archive and whether the bootstrap
loader should be included or not. The following layouts are available:
@@ -530,6 +530,37 @@ loader should be included or not. The following layouts are available:
+[[build-tool-plugins-gradle-configuration-custom-layout]]
+==== Using a custom layout
+If you have custom requirements for how to arrange the dependencies and loader classes
+inside the repackaged jar, you can use a custom layout in addition to the built-in values.
+Any library which defines one or more `LayoutFactory` implementations and
+lists them in `META-INF/spring.factories` can be added to the build script dependencies
+and then the layout type becomes available in the `springBoot` configuration. For example
+
+[source,groovy,indent=0,subs="verbatim,attributes"]
+----
+buildscript {
+ ext {
+ springBootVersion = '1.5.0.BUILD-SNAPSHOT'
+ customVersion = '0.0.1.BUILD-SNAPSHOT'
+ }
+ repositories {
+ mavenLocal()
+ mavenCentral()
+ }
+ dependencies {
+ classpath("com.example:custom-layout:${customVersion}")
+ classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
+ }
+}
+
+springBoot {
+ layout = 'CUSTOM'
+}
+----
+
+
[[build-tool-plugins-understanding-the-gradle-plugin]]
=== Understanding how the Gradle plugin works
When `spring-boot` is applied to your Gradle project a default task named `bootRepackage`
diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/SpringBootPluginExtension.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/SpringBootPluginExtension.java
index 9b250d9cbc..d84bb2134c 100644
--- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/SpringBootPluginExtension.java
+++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/SpringBootPluginExtension.java
@@ -20,6 +20,8 @@ import java.io.File;
import java.util.Map;
import java.util.Set;
+import groovy.lang.Closure;
+
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaPlugin;
@@ -27,8 +29,6 @@ import org.springframework.boot.gradle.buildinfo.BuildInfo;
import org.springframework.boot.loader.tools.Layout;
import org.springframework.boot.loader.tools.LayoutType;
-import groovy.lang.Closure;
-
/**
* Gradle DSL Extension for 'Spring Boot'. Most of the time Spring Boot can guess the
* settings in this extension, but occasionally you might need to explicitly set one or
diff --git a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/JarWriter.java b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/JarWriter.java
index d5a05dd094..929ef645dc 100644
--- a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/JarWriter.java
+++ b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/JarWriter.java
@@ -53,8 +53,6 @@ import org.springframework.lang.UsesJava7;
*/
public class JarWriter {
- private static final String NESTED_LOADER_JAR = "META-INF/loader/spring-boot-loader.jar";
-
private static final int BUFFER_SIZE = 32 * 1024;
private final JarOutputStream jarOutput;
@@ -206,9 +204,20 @@ public class JarWriter {
/**
* Write the required spring-boot-loader classes to the JAR.
* @throws IOException if the classes cannot be written
+ * @deprecated us {@link #writeLoaderClasses(String)} instead
*/
+ @Deprecated
public void writeLoaderClasses() throws IOException {
- URL loaderJar = getClass().getClassLoader().getResource(NESTED_LOADER_JAR);
+ writeLoaderClasses(Layouts.DEFAULT_LOADER_JAR);
+ }
+
+ /**
+ * Write the required spring-boot-loader classes to the JAR.
+ * @param loaderJarPath the path to the loader jar (in the classpath)
+ * @throws IOException if the classes cannot be written
+ */
+ public void writeLoaderClasses(String loaderJarPath) throws IOException {
+ URL loaderJar = getClass().getClassLoader().getResource(loaderJarPath);
JarInputStream inputStream = new JarInputStream(
new BufferedInputStream(loaderJar.openStream()));
JarEntry entry;
diff --git a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Layout.java b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Layout.java
index 38681e107e..09f7ded6be 100644
--- a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Layout.java
+++ b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Layout.java
@@ -40,15 +40,25 @@ public interface Layout {
String getLibraryDestination(String libraryName, LibraryScope scope);
/**
- * Returns the location of classes within the archive.
+ * Returns the location of classes within the archive. Empty if the location is the
+ * root path, otherwise ends with a slash ('/').
* @return the classes location
*/
String getClassesLocation();
/**
- * Returns if loader classes should be included to make the archive executable.
+ * Returns if loader classes should be included to make the archive executable. If
+ * true, then {@link #getLoaderJarPath()} should point to a valid jar file that
+ * contains the loader classes.
* @return if the layout is executable
*/
boolean isExecutable();
+ /**
+ * Returns the path to a nested jar that contains the loader, and which will be
+ * unpacked into the root of the repackaged jar.
+ * @return the path to a nested jar that contains the loader
+ */
+ String getLoaderJarPath();
+
}
diff --git a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/LayoutFactory.java b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/LayoutFactory.java
index 66c5738f86..9b21eceeb3 100644
--- a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/LayoutFactory.java
+++ b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/LayoutFactory.java
@@ -18,14 +18,14 @@ package org.springframework.boot.loader.tools;
/**
* Strategy for creating instances of {@link Layout}.
- *
+ *
* @author Dave Syer
*
*/
public interface LayoutFactory {
-
+
Layout getLayout();
-
+
String getName();
}
diff --git a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/LayoutType.java b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/LayoutType.java
index ff101007fe..6901a21117 100644
--- a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/LayoutType.java
+++ b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/LayoutType.java
@@ -24,6 +24,8 @@ import org.springframework.core.io.support.SpringFactoriesLoader;
/**
* Archive layout types.
+ *
+ * @author Dave Syer
*/
public enum LayoutType {
diff --git a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Layouts.java b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Layouts.java
index 3cebdc7a05..992a6ab40e 100644
--- a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Layouts.java
+++ b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Layouts.java
@@ -33,6 +33,11 @@ import java.util.Set;
*/
public final class Layouts {
+ /**
+ * Default value for {@link #getLoaderJarPath()}.
+ */
+ public static final String DEFAULT_LOADER_JAR = "META-INF/loader/spring-boot-loader.jar";
+
private Layouts() {
}
@@ -87,6 +92,11 @@ public final class Layouts {
return true;
}
+ @Override
+ public String getLoaderJarPath() {
+ return DEFAULT_LOADER_JAR;
+ }
+
}
/**
@@ -116,6 +126,11 @@ public final class Layouts {
return false;
}
+ @Override
+ public String getLoaderJarPath() {
+ return DEFAULT_LOADER_JAR;
+ }
+
}
/**
@@ -154,6 +169,11 @@ public final class Layouts {
return true;
}
+ @Override
+ public String getLoaderJarPath() {
+ return DEFAULT_LOADER_JAR;
+ }
+
}
/**
@@ -188,6 +208,11 @@ public final class Layouts {
return false;
}
+ @Override
+ public String getLoaderJarPath() {
+ return DEFAULT_LOADER_JAR;
+ }
+
}
}
diff --git a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Repackager.java b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Repackager.java
index c25129844b..757ece7071 100644
--- a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Repackager.java
+++ b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Repackager.java
@@ -217,7 +217,7 @@ public class Repackager {
}
writeNestedLibraries(standardLibraries, seen, writer);
if (this.layout.isExecutable()) {
- writer.writeLoaderClasses();
+ writer.writeLoaderClasses(this.layout.getLoaderJarPath());
}
}
finally {
diff --git a/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/LayoutTypeTests.java b/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/LayoutTypeTests.java
index 5fc5135419..3792529f22 100644
--- a/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/LayoutTypeTests.java
+++ b/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/LayoutTypeTests.java
@@ -28,14 +28,15 @@ public class LayoutTypeTests {
@Test
public void standardType() {
- assertThat(LayoutType.layout("DIR")).isEqualTo(LayoutType.valueOf("DIR").layout());
+ assertThat(LayoutType.layout("DIR"))
+ .isEqualTo(LayoutType.valueOf("DIR").layout());
}
@Test
public void customType() {
assertThat(LayoutType.layout("CUSTOM")).isNotNull();
}
-
+
public static class TestLayoutFactory implements LayoutFactory {
@Override
@@ -46,6 +47,7 @@ public class LayoutTypeTests {
@Override
public String getName() {
return "CUSTOM";
- }}
+ }
+ }
}
diff --git a/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/RepackageMojo.java b/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/RepackageMojo.java
index 2e07cba4a0..42de1700b4 100644
--- a/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/RepackageMojo.java
+++ b/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/RepackageMojo.java
@@ -227,6 +227,7 @@ public class RepackageMojo extends AbstractDependencyFilterMojo {
repackager.setMainClass(this.mainClass);
if (this.layout != null) {
getLog().info("Layout: " + this.layout);
+ repackager.setLayout(LayoutType.layout(this.layout));
}
return repackager;
}
diff --git a/spring-boot-tools/spring-boot-maven-plugin/src/site/apt/examples/custom-layout.apt.vm b/spring-boot-tools/spring-boot-maven-plugin/src/site/apt/examples/custom-layout.apt.vm
new file mode 100644
index 0000000000..031e29ff71
--- /dev/null
+++ b/spring-boot-tools/spring-boot-maven-plugin/src/site/apt/examples/custom-layout.apt.vm
@@ -0,0 +1,57 @@
+ -----
+ Use a custom layout
+ -----
+ Dave Syer
+ -----
+ 2016-10-30
+ -----
+
+ Spring Boot repackages the jar file for this project using a custom
+ layout defined in the additional jar file, provided as a dependency
+ to the build plugin:
+
+---
+
+ ...
+
+ ...
+
+ ...
+
+ ${project.groupId}
+ ${project.artifactId}
+ ${project.version}
+
+
+
+ repackage
+
+
+ CUSTOM
+
+
+
+
+
+ com.example
+ custom-layout
+ 0.0.1.BUILD-SNAPSHOT
+
+
+ ...
+
+ ...
+
+ ...
+
+ ...
+
+---
+
+ The layout is provided as an implementation of <>
+ (from spring-boot-loader-tools) listed in
+ <> inside the <> jar.
+
+
+
+
diff --git a/spring-boot-tools/spring-boot-maven-plugin/src/site/apt/index.apt b/spring-boot-tools/spring-boot-maven-plugin/src/site/apt/index.apt
index dcf5084405..cb767f9e64 100644
--- a/spring-boot-tools/spring-boot-maven-plugin/src/site/apt/index.apt
+++ b/spring-boot-tools/spring-boot-maven-plugin/src/site/apt/index.apt
@@ -48,6 +48,8 @@ Spring Boot Maven Plugin
* {{{./examples/repackage-disable-attach.html}Local repackaged artifact}}
+ * {{{./examples/custom-layout.html}Custom layout}}
+
* {{{./examples/exclude-dependency.html}Exclude a dependency}}
* {{{./examples/run-debug.html}Debug the application}}