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..aea50dc776 100644 --- a/spring-boot-docs/src/main/asciidoc/build-tool-plugins.adoc +++ b/spring-boot-docs/src/main/asciidoc/build-tool-plugins.adoc @@ -484,6 +484,11 @@ The following configuration options are available: (defaults to a guess based on the archive type). See <>. +|'layoutFactory` +|A layout factory that can be used if a custom layout is required. Alternative layouts +can be provided by 3rd parties. Layout factories are only used when `layout` is not +specified. + |`requiresUnpack` |A list of dependencies (in the form "`groupId:artifactId`" that must be unpacked from fat jars in order to run. Items are still packaged into the fat jar, but they will be @@ -530,6 +535,38 @@ loader should be included or not. The following layouts are available: ++[[build-tool-plugins-gradle-configuration-custom-repackager]] ++==== Using a custom layout +If you have custom requirements for how to arrange the dependencies and loader classes +inside the repackaged jar, you can use a custom layout. Any library which defines one +or more `LayoutFactory` implementations can be added to the build script dependencies +and then the layout factory becomes available in the `springBoot` configuration. +For example: + +[source,groovy,indent=0,subs="verbatim,attributes"] +---- +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath("org.springframework.boot:spring-boot-gradle-plugin:{spring-boot-version}") + classpath("com.example:custom-layout:1.0.0") + } +} + +springBoot { + layoutFactory = new com.example.CustomLayoutFactory() +} ++---- + +NOTE: If there is only one custom `LayoutFactory` on the build classpath and it is +listed in `META-INF/spring.factories` then it is unnecessary to explicitly set it in the +`springBoot` configuration. Layout factories are only used when no explicit `layout` is +specified. + + + [[build-tool-plugins-understanding-the-gradle-plugin]] === Understanding how the Gradle plugin works When `spring-boot` is applied to your Gradle project a default task named `bootRepackage` 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 987336250c..3ddd57c5a5 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 @@ -26,6 +26,7 @@ import org.gradle.api.plugins.JavaPlugin; import org.springframework.boot.gradle.buildinfo.BuildInfo; import org.springframework.boot.loader.tools.Layout; +import org.springframework.boot.loader.tools.LayoutFactory; import org.springframework.boot.loader.tools.Layouts; /** @@ -90,6 +91,12 @@ public class SpringBootPluginExtension { */ LayoutType layout; + /** + * The layout factory that will be used when no explicit layout is specified. + * Alternative layouts can be provided by 3rd parties. + */ + LayoutFactory layoutFactory; + /** * Libraries that must be unpacked from fat jars in order to run. Use Strings in the * form {@literal groupId:artifactId}. @@ -196,6 +203,14 @@ public class SpringBootPluginExtension { this.layout = layout; } + public LayoutFactory getLayoutFactory() { + return this.layoutFactory; + } + + public void setLayoutFactory(LayoutFactory layoutFactory) { + this.layoutFactory = layoutFactory; + } + public Set getRequiresUnpack() { return this.requiresUnpack; } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/repackage/RepackageTask.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/repackage/RepackageTask.java index 98d17ccb88..4e78f2478b 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/repackage/RepackageTask.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/repackage/RepackageTask.java @@ -213,7 +213,8 @@ public class RepackageTask extends DefaultTask { copy(file, outputFile); file = outputFile; } - Repackager repackager = new Repackager(file); + Repackager repackager = new Repackager(file, + this.extension.getLayoutFactory()); repackager.addMainClassTimeoutWarningListener( new LoggingMainClassTimeoutWarningListener()); setMainClass(repackager); diff --git a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/CustomLoaderLayout.java b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/CustomLoaderLayout.java new file mode 100644 index 0000000000..746a12fd3a --- /dev/null +++ b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/CustomLoaderLayout.java @@ -0,0 +1,37 @@ +/* + * Copyright 2012-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.loader.tools; + +import java.io.IOException; + +/** + * Additional interface that can be implemented by {@link Layout Layouts} that write their + * own loader classes. + * + * @author Phillip Webb + * @since 1.5.0 + */ +public interface CustomLoaderLayout { + + /** + * Write the required loader classes into the JAR. + * @param writer the writer used to write the classes + * @throws IOException if the classes cannot be written + */ + void writeLoadedClasses(LoaderClassesWriter writer) throws IOException; + +} diff --git a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/DefaultLayoutFactory.java b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/DefaultLayoutFactory.java new file mode 100644 index 0000000000..b9d196dbed --- /dev/null +++ b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/DefaultLayoutFactory.java @@ -0,0 +1,34 @@ +/* + * Copyright 2012-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.loader.tools; + +import java.io.File; + +/** + * Default implementation of {@link LayoutFactory}. + * + * @author Phillip Webb + * @since 1.5.0 + */ +public class DefaultLayoutFactory implements LayoutFactory { + + @Override + public Layout getLayout(File source) { + return Layouts.forFile(source); + } + +} 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..5f7ba00c84 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 @@ -51,7 +51,7 @@ import org.springframework.lang.UsesJava7; * @author Phillip Webb * @author Andy Wilkinson */ -public class JarWriter { +public class JarWriter implements LoaderClassesWriter { private static final String NESTED_LOADER_JAR = "META-INF/loader/spring-boot-loader.jar"; @@ -158,6 +158,7 @@ public class JarWriter { * @param inputStream The stream from which the entry's data can be read * @throws IOException if the write fails */ + @Override public void writeEntry(String entryName, InputStream inputStream) throws IOException { JarEntry entry = new JarEntry(entryName); writeEntry(entry, new InputStreamEntryWriter(inputStream, true)); @@ -207,8 +208,20 @@ public class JarWriter { * Write the required spring-boot-loader classes to the JAR. * @throws IOException if the classes cannot be written */ + @Override public void writeLoaderClasses() throws IOException { - URL loaderJar = getClass().getClassLoader().getResource(NESTED_LOADER_JAR); + writeLoaderClasses(NESTED_LOADER_JAR); + } + + /** + * Write the required spring-boot-loader classes to the JAR. + * @param loaderJarResourceName the name of the resource containing the loader classes + * to be written + * @throws IOException if the classes cannot be written + */ + @Override + public void writeLoaderClasses(String loaderJarResourceName) throws IOException { + URL loaderJar = getClass().getClassLoader().getResource(loaderJarResourceName); 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..5c13528fb9 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 @@ -18,9 +18,13 @@ package org.springframework.boot.loader.tools; /** * Strategy interface used to determine the layout for a particular type of archive. + * Layouts may additionally implement {@link CustomLoaderLayout} if they wish to write + * custom loader classes. * * @author Phillip Webb * @see Layouts + * @see RepackagingLayout + * @see CustomLoaderLayout */ public interface Layout { 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 new file mode 100644 index 0000000000..0a6e7eeb33 --- /dev/null +++ b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/LayoutFactory.java @@ -0,0 +1,36 @@ +/* + * Copyright 2012-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.loader.tools; + +import java.io.File; + +/** + * Factory interface used to create a {@link Layout}. + * + * @author Dave Syer + * @author Phillip Webb + */ +public interface LayoutFactory { + + /** + * Return a {@link Layout} for the specified source file. + * @param source the source file + * @return the layout to use for the file + */ + Layout getLayout(File source); + +} diff --git a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/LoaderClassesWriter.java b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/LoaderClassesWriter.java new file mode 100644 index 0000000000..6e2324de8e --- /dev/null +++ b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/LoaderClassesWriter.java @@ -0,0 +1,53 @@ +/* + * Copyright 2012-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.loader.tools; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Writer used by {@link CustomLoaderLayout CustomLoaderLayouts} to write classes into a + * repackaged JAR. + * + * @author Phillip Webb + * @since 1.5.0 + */ +public interface LoaderClassesWriter { + + /** + * Write the default required spring-boot-loader classes to the JAR. + * @throws IOException if the classes cannot be written + */ + void writeLoaderClasses() throws IOException; + + /** + * Write custom required spring-boot-loader classes to the JAR. + * @param loaderJarResourceName the name of the resource containing the loader classes + * to be written + * @throws IOException if the classes cannot be written + */ + void writeLoaderClasses(String loaderJarResourceName) throws IOException; + + /** + * Write a single entry to the JAR. + * @param name the name of the entry + * @param inputStream the input stream content + * @throws IOException if the entry cannot be written + */ + void writeEntry(String name, InputStream inputStream) throws IOException; + +} 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 ed56ebe5fc..8c7cfb1ac7 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 @@ -30,7 +30,10 @@ import java.util.jar.JarFile; import java.util.jar.Manifest; import org.springframework.boot.loader.tools.JarWriter.EntryTransformer; +import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.lang.UsesJava8; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; /** * Utility class that can be used to repackage an archive so that it can be executed using @@ -66,12 +69,18 @@ public class Repackager { private Layout layout; + private LayoutFactory layoutFactory; + public Repackager(File source) { + this(source, null); + } + + public Repackager(File source, LayoutFactory layoutFactory) { if (source == null || !source.exists() || !source.isFile()) { throw new IllegalArgumentException("Source must refer to an existing file"); } this.source = source.getAbsoluteFile(); - this.layout = Layouts.forFile(source); + this.layoutFactory = layoutFactory; } /** @@ -113,6 +122,15 @@ public class Repackager { this.layout = layout; } + /** + * Sets the layout factory for the jar. The factory can be used when no specific + * layout is specific. + * @param layoutFactory the layoutFactory to set + */ + public void setLayoutFactory(LayoutFactory layoutFactory) { + this.layoutFactory = layoutFactory; + } + /** * Repackage the source file so that it can be run using '{@literal java -jar}'. * @param libraries the libraries required to run the archive @@ -150,6 +168,9 @@ public class Repackager { if (libraries == null) { throw new IllegalArgumentException("Libraries must not be null"); } + if (this.layout == null) { + this.layout = getLayoutFactory().getLayout(this.source); + } if (alreadyRepackaged()) { return; } @@ -177,6 +198,19 @@ public class Repackager { } } + private LayoutFactory getLayoutFactory() { + if (this.layoutFactory != null) { + return this.layoutFactory; + } + List factories = SpringFactoriesLoader + .loadFactories(LayoutFactory.class, null); + if (factories.isEmpty()) { + return new DefaultLayoutFactory(); + } + Assert.state(factories.size() == 1, "No unique LayoutFactory found"); + return factories.get(0); + } + /** * Return the {@link File} to use to backup the original source. * @return the file to use to backup the original source @@ -219,21 +253,7 @@ public class Repackager { } }); - writer.writeManifest(buildManifest(sourceJar)); - Set seen = new HashSet(); - writeNestedLibraries(unpackLibraries, seen, writer); - if (this.layout instanceof RepackagingLayout) { - writer.writeEntries(sourceJar, - new RenamingEntryTransformer(((RepackagingLayout) this.layout) - .getRepackagedClassesLocation())); - } - else { - writer.writeEntries(sourceJar); - } - writeNestedLibraries(standardLibraries, seen, writer); - if (this.layout.isExecutable()) { - writer.writeLoaderClasses(); - } + repackage(sourceJar, writer, unpackLibraries, standardLibraries); } finally { try { @@ -245,6 +265,23 @@ public class Repackager { } } + private void repackage(JarFile sourceJar, JarWriter writer, + final List unpackLibraries, final List standardLibraries) + throws IOException { + writer.writeManifest(buildManifest(sourceJar)); + Set seen = new HashSet(); + writeNestedLibraries(unpackLibraries, seen, writer); + if (this.layout instanceof RepackagingLayout) { + writer.writeEntries(sourceJar, new RenamingEntryTransformer( + ((RepackagingLayout) this.layout).getRepackagedClassesLocation())); + } + else { + writer.writeEntries(sourceJar); + } + writeNestedLibraries(standardLibraries, seen, writer); + writeLoaderClasses(writer); + } + private void writeNestedLibraries(List libraries, Set alreadySeen, JarWriter writer) throws IOException { for (Library library : libraries) { @@ -260,6 +297,15 @@ public class Repackager { } } + private void writeLoaderClasses(JarWriter writer) throws IOException { + if (this.layout instanceof CustomLoaderLayout) { + ((CustomLoaderLayout) this.layout).writeLoadedClasses(writer); + } + else if (this.layout.isExecutable()) { + writer.writeLoaderClasses(); + } + } + private boolean isZip(File file) { try { FileInputStream fileInputStream = new FileInputStream(file); @@ -316,8 +362,10 @@ public class Repackager { (this.layout instanceof RepackagingLayout) ? ((RepackagingLayout) this.layout).getRepackagedClassesLocation() : this.layout.getClassesLocation()); - manifest.getMainAttributes().putValue(BOOT_LIB_ATTRIBUTE, - this.layout.getLibraryDestination("", LibraryScope.COMPILE)); + String lib = this.layout.getLibraryDestination("", LibraryScope.COMPILE); + if (StringUtils.hasLength(lib)) { + manifest.getMainAttributes().putValue(BOOT_LIB_ATTRIBUTE, lib); + } return manifest; } diff --git a/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/RepackagerTests.java b/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/RepackagerTests.java index 4c3ee88ea6..beb8203eb5 100644 --- a/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/RepackagerTests.java +++ b/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/RepackagerTests.java @@ -16,6 +16,7 @@ package org.springframework.boot.loader.tools; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -347,14 +348,46 @@ public class RepackagerTests { final LibraryScope scope = mock(LibraryScope.class); given(layout.getLauncherClassName()).willReturn("testLauncher"); given(layout.getLibraryDestination(anyString(), eq(scope))).willReturn("test/"); + given(layout.getLibraryDestination(anyString(), eq(LibraryScope.COMPILE))) + .willReturn("test-lib/"); repackager.setLayout(layout); repackager.repackage(new Libraries() { + @Override public void doWithLibraries(LibraryCallback callback) throws IOException { callback.library(new Library(libJarFile, scope)); } + }); assertThat(hasEntry(file, "test/" + libJarFile.getName())).isTrue(); + assertThat(getManifest(file).getMainAttributes().getValue("Spring-Boot-Lib")) + .isEqualTo("test-lib/"); + assertThat(getManifest(file).getMainAttributes().getValue("Main-Class")) + .isEqualTo("testLauncher"); + } + + @Test + public void customLayoutNoBootLib() throws Exception { + TestJarFile libJar = new TestJarFile(this.temporaryFolder); + libJar.addClass("a/b/C.class", ClassWithoutMainMethod.class); + final File libJarFile = libJar.getFile(); + this.testJarFile.addClass("a/b/C.class", ClassWithMainMethod.class); + File file = this.testJarFile.getFile(); + Repackager repackager = new Repackager(file); + Layout layout = mock(Layout.class); + final LibraryScope scope = mock(LibraryScope.class); + given(layout.getLauncherClassName()).willReturn("testLauncher"); + repackager.setLayout(layout); + repackager.repackage(new Libraries() { + + @Override + public void doWithLibraries(LibraryCallback callback) throws IOException { + callback.library(new Library(libJarFile, scope)); + } + + }); + assertThat(getManifest(file).getMainAttributes().getValue("Spring-Boot-Lib")) + .isNull(); assertThat(getManifest(file).getMainAttributes().getValue("Main-Class")) .isEqualTo("testLauncher"); } @@ -536,6 +569,29 @@ public class RepackagerTests { } } + @Test + public void customLayoutFactoryWithoutLayout() throws Exception { + this.testJarFile.addClass("a/b/C.class", ClassWithMainMethod.class); + File source = this.testJarFile.getFile(); + Repackager repackager = new Repackager(source, new TestLayoutFactory()); + repackager.repackage(NO_LIBRARIES); + JarFile jarFile = new JarFile(source); + assertThat(jarFile.getEntry("test")).isNotNull(); + jarFile.close(); + } + + @Test + public void customLayoutFactoryWithLayout() throws Exception { + this.testJarFile.addClass("a/b/C.class", ClassWithMainMethod.class); + File source = this.testJarFile.getFile(); + Repackager repackager = new Repackager(source, new TestLayoutFactory()); + repackager.setLayout(new Layouts.Jar()); + repackager.repackage(NO_LIBRARIES); + JarFile jarFile = new JarFile(source); + assertThat(jarFile.getEntry("test")).isNull(); + jarFile.close(); + } + private boolean hasLauncherClasses(File file) throws IOException { return hasEntry(file, "org/springframework/boot/") && hasEntry(file, "org/springframework/boot/loader/JarLauncher.class"); @@ -580,4 +636,22 @@ public class RepackagerTests { } + public static class TestLayoutFactory implements LayoutFactory { + + @Override + public Layout getLayout(File source) { + return new TestLayout(); + } + + } + + private static class TestLayout extends Layouts.Jar implements CustomLoaderLayout { + + @Override + public void writeLoadedClasses(LoaderClassesWriter writer) throws IOException { + writer.writeEntry("test", new ByteArrayInputStream("test".getBytes())); + } + + } + } 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 4c7092d17a..7569d02142 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 @@ -40,6 +40,7 @@ import org.apache.maven.shared.artifact.filter.collection.ScopeFilter; import org.springframework.boot.loader.tools.DefaultLaunchScript; import org.springframework.boot.loader.tools.LaunchScript; import org.springframework.boot.loader.tools.Layout; +import org.springframework.boot.loader.tools.LayoutFactory; import org.springframework.boot.loader.tools.Layouts; import org.springframework.boot.loader.tools.Libraries; import org.springframework.boot.loader.tools.Repackager; @@ -129,6 +130,15 @@ public class RepackageMojo extends AbstractDependencyFilterMojo { @Parameter private LayoutType layout; + /** + * The layout factory that will be used to create the executable archive if no + * explicit layout is set. Alternative layouts implementations can be provided by 3rd + * parties. + * @since 1.5 + */ + @Parameter + private LayoutFactory layoutFactory; + /** * A list of the libraries that must be unpacked from fat jars in order to run. * Specify each library as a <dependency> with a @@ -220,7 +230,7 @@ public class RepackageMojo extends AbstractDependencyFilterMojo { } private Repackager getRepackager(File source) { - Repackager repackager = new Repackager(source); + Repackager repackager = new Repackager(source, this.layoutFactory); repackager.addMainClassTimeoutWarningListener( new LoggingMainClassTimeoutWarningListener()); repackager.setMainClass(this.mainClass); diff --git a/spring-boot-tools/spring-boot-maven-plugin/src/site/apt/examples/custom-layout.apt b/spring-boot-tools/spring-boot-maven-plugin/src/site/apt/examples/custom-layout.apt new file mode 100644 index 0000000000..f4b5809286 --- /dev/null +++ b/spring-boot-tools/spring-boot-maven-plugin/src/site/apt/examples/custom-layout.apt @@ -0,0 +1,58 @@ + ----- + Use a custom layout + ----- + Dave Syer + ----- + 2016-10-30 + ----- + + Spring Boot repackages the jar file for this project using a custom layout factory + defined in the additional jar file, provided as a dependency to the build plugin: + +--- + + ... + + ... + + ... + + ${project.groupId} + ${project.artifactId} + ${project.version} + + + + repackage + + + + value + + + + + + + com.example + custom-layout + 0.0.1.BUILD-SNAPSHOT + + + ... + + ... + + ... + + ... + ++--- + + The layout factory is provided as an implementation of <<>> (from + spring-boot-loader-tools) explicitly specified in the pom. If there is only one custom + <<>> on the plugin classpath and it is listed in + <<>> then it is unnecessary to explicitly set it in the + plugin configuration. + + Layout factories are always ignored if an explicit <> is set. 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 033d484e26..fca3d4f6f7 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}}