From a5c372ac8150b9d7f3ba8c83c829850b11093cd9 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Sun, 30 Oct 2016 11:53:24 +0000 Subject: [PATCH 1/6] Ensure Spring-Boot-Lib is never "null" --- .../boot/loader/tools/Repackager.java | 7 +++-- .../boot/loader/tools/RepackagerTests.java | 31 +++++++++++++++++-- 2 files changed, 34 insertions(+), 4 deletions(-) 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 c25129844bbb..988e4ba0d487 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 @@ -301,8 +301,11 @@ else if (startClass != null) { (this.layout instanceof RepackagingLayout) ? ((RepackagingLayout) this.layout).getRepackagedClassesLocation() : this.layout.getClassesLocation()); - manifest.getMainAttributes().putValue(BOOT_LIB_ATTRIBUTE, - this.layout.getLibraryDestination("", LibraryScope.COMPILE)); + String libraryDestination = this.layout.getLibraryDestination("", + LibraryScope.COMPILE); + if (libraryDestination != null) { + manifest.getMainAttributes().putValue(BOOT_LIB_ATTRIBUTE, libraryDestination); + } 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 4c3ee88ea617..0f8225ccad00 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 @@ -40,8 +40,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; +import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; /** @@ -346,7 +346,8 @@ public void customLayout() throws Exception { Layout layout = mock(Layout.class); final LibraryScope scope = mock(LibraryScope.class); given(layout.getLauncherClassName()).willReturn("testLauncher"); - given(layout.getLibraryDestination(anyString(), eq(scope))).willReturn("test/"); + given(layout.getLibraryDestination(anyString(), any(LibraryScope.class))) + .willReturn("test/"); repackager.setLayout(layout); repackager.repackage(new Libraries() { @Override @@ -355,6 +356,32 @@ public void doWithLibraries(LibraryCallback callback) throws IOException { } }); assertThat(hasEntry(file, "test/" + libJarFile.getName())).isTrue(); + assertThat(getManifest(file).getMainAttributes().getValue("Spring-Boot-Lib")) + .isEqualTo("test/"); + 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()) + .doesNotContainKey("Spring-Boot-Lib"); assertThat(getManifest(file).getMainAttributes().getValue("Main-Class")) .isEqualTo("testLauncher"); } From 570a0404784e1f9afcef96b1408b249ecd7b8116 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Fri, 28 Oct 2016 13:53:51 +0100 Subject: [PATCH 2/6] Add LayoutFactory in spring.factories Instead of a fixed enum of layout types, user can provide custom layouts via implementations of LayoutFactory in spring.factories. --- .../gradle/SpringBootPluginExtension.java | 38 ++----- .../boot/loader/tools/LayoutFactory.java | 31 ++++++ .../boot/loader/tools/LayoutType.java | 99 +++++++++++++++++++ .../boot/loader/tools/LayoutTypeTests.java | 51 ++++++++++ .../test/resources/META-INF/spring.factories | 2 + .../boot/maven/RepackageMojo.java | 53 +--------- 6 files changed, 192 insertions(+), 82 deletions(-) create mode 100644 spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/LayoutFactory.java create mode 100644 spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/LayoutType.java create mode 100644 spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/LayoutTypeTests.java create mode 100644 spring-boot-tools/spring-boot-loader-tools/src/test/resources/META-INF/spring.factories 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 987336250ccb..9b250d9cbc42 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,13 +20,14 @@ import java.util.Map; import java.util.Set; -import groovy.lang.Closure; import org.gradle.api.Project; 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.Layouts; +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 @@ -88,7 +89,7 @@ public class SpringBootPluginExtension { * the MANIFEST.MF 'Main-Class' to be PropertiesLauncher. Gradle will coerce literal * String values to the correct type. */ - LayoutType layout; + String layout; /** * Libraries that must be unpacked from fat jars in order to run. Use Strings in the @@ -145,7 +146,7 @@ public SpringBootPluginExtension(Project project) { * @return the Layout to use or null if not explicitly set */ public Layout convertLayout() { - return (this.layout == null ? null : this.layout.layout); + return (this.layout == null ? null : LayoutType.layout(this.layout)); } public String getMainClass() { @@ -188,11 +189,11 @@ public void setBackupSource(boolean backupSource) { this.backupSource = backupSource; } - public LayoutType getLayout() { + public String getLayout() { return this.layout; } - public void setLayout(LayoutType layout) { + public void setLayout(String layout) { this.layout = layout; } @@ -276,29 +277,4 @@ public void buildInfo(Closure taskConfigurer) { } } - /** - * Layout Types. - */ - enum LayoutType { - - JAR(new Layouts.Jar()), - - WAR(new Layouts.War()), - - ZIP(new Layouts.Expanded()), - - DIR(new Layouts.Expanded()), - - MODULE(new Layouts.Module()), - - NONE(new Layouts.None()); - - Layout layout; - - LayoutType(Layout layout) { - this.layout = 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 000000000000..66c5738f8611 --- /dev/null +++ b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/LayoutFactory.java @@ -0,0 +1,31 @@ +/* + * Copyright 2012-2015 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; + +/** + * 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 new file mode 100644 index 000000000000..ff101007fed9 --- /dev/null +++ b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/LayoutType.java @@ -0,0 +1,99 @@ +/* + * Copyright 2012-2015 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.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.core.io.support.SpringFactoriesLoader; + +/** + * Archive layout types. + */ +public enum LayoutType { + + /** + * Jar Layout. + */ + JAR(new Layouts.Jar()), + + /** + * War Layout. + */ + WAR(new Layouts.War()), + + /** + * Zip Layout. + */ + ZIP(new Layouts.Expanded()), + + /** + * Dir Layout. + */ + DIR(new Layouts.Expanded()), + + /** + * Module Layout. + */ + MODULE(new Layouts.Module()), + + /** + * No Layout. + */ + NONE(new Layouts.None()); + + private static Map customTypes; + + private final Layout layout; + + public Layout layout() { + return this.layout; + } + + LayoutType(Layout layout) { + this.layout = layout; + } + + public static Layout layout(String value) { + try { + return valueOf(value).layout(); + } + catch (IllegalArgumentException e) { + if (customTypes == null) { + customTypes = new HashMap(); + lookupCustomTypes(); + } + Layout layout = customTypes.get(value); + if (layout == null) { + throw new IllegalArgumentException( + "Cannot resolve custom layout type: " + value); + } + return layout; + } + } + + private static void lookupCustomTypes() { + ClassLoader classLoader = LayoutType.class.getClassLoader(); + List factories = SpringFactoriesLoader + .loadFactories(LayoutFactory.class, classLoader); + for (LayoutFactory factory : factories) { + customTypes.put(factory.getName(), factory.getLayout()); + } + } + +} 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 new file mode 100644 index 000000000000..5fc5135419b1 --- /dev/null +++ b/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/LayoutTypeTests.java @@ -0,0 +1,51 @@ +/* + * Copyright 2012-2015 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 org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Dave Syer + * + */ +public class LayoutTypeTests { + + @Test + public void standardType() { + 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 + public Layout getLayout() { + return new Layouts.Jar(); + } + + @Override + public String getName() { + return "CUSTOM"; + }} + +} diff --git a/spring-boot-tools/spring-boot-loader-tools/src/test/resources/META-INF/spring.factories b/spring-boot-tools/spring-boot-loader-tools/src/test/resources/META-INF/spring.factories new file mode 100644 index 000000000000..37eac70aa358 --- /dev/null +++ b/spring-boot-tools/spring-boot-loader-tools/src/test/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.loader.tools.LayoutFactory=\ +org.springframework.boot.loader.tools.LayoutTypeTests.TestLayoutFactory \ No newline at end of file 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 309eb72db37d..2e07cba4a0ad 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 @@ -42,8 +42,7 @@ 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.Layouts; +import org.springframework.boot.loader.tools.LayoutType; import org.springframework.boot.loader.tools.Libraries; import org.springframework.boot.loader.tools.Repackager; @@ -131,7 +130,7 @@ public class RepackageMojo extends AbstractDependencyFilterMojo { * @since 1.0 */ @Parameter - private LayoutType layout; + private String layout; /** * A list of the libraries that must be unpacked from fat jars in order to run. @@ -228,7 +227,6 @@ private Repackager getRepackager(File source) { repackager.setMainClass(this.mainClass); if (this.layout != null) { getLog().info("Layout: " + this.layout); - repackager.setLayout(this.layout.layout()); } return repackager; } @@ -309,53 +307,6 @@ else if (!source.equals(repackaged)) { } } - /** - * Archive layout types. - */ - public enum LayoutType { - - /** - * Jar Layout. - */ - JAR(new Layouts.Jar()), - - /** - * War Layout. - */ - WAR(new Layouts.War()), - - /** - * Zip Layout. - */ - ZIP(new Layouts.Expanded()), - - /** - * Dir Layout. - */ - DIR(new Layouts.Expanded()), - - /** - * Module Layout. - */ - MODULE(new Layouts.Module()), - - /** - * No Layout. - */ - NONE(new Layouts.None()); - - private final Layout layout; - - public Layout layout() { - return this.layout; - } - - LayoutType(Layout layout) { - this.layout = layout; - } - - } - private static class LoggingRepackager extends Repackager { private final Log log; From 222bc2afb5cda271f96d778c10dfb834dbb71fdc Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Fri, 28 Oct 2016 17:33:03 +0100 Subject: [PATCH 3/6] Support for custom layout types to change loader classes A layout can also optionally change the loader jar that is unpacked in the root of the repackaged archive by implementing a new method in Layout. --- .../src/main/asciidoc/build-tool-plugins.adoc | 33 ++++++++++- .../gradle/SpringBootPluginExtension.java | 4 +- .../boot/loader/tools/JarWriter.java | 15 ++++- .../boot/loader/tools/Layout.java | 14 ++++- .../boot/loader/tools/LayoutFactory.java | 6 +- .../boot/loader/tools/LayoutType.java | 2 + .../boot/loader/tools/Layouts.java | 25 ++++++++ .../boot/loader/tools/Repackager.java | 2 +- .../boot/loader/tools/LayoutTypeTests.java | 8 ++- .../boot/maven/RepackageMojo.java | 1 + .../site/apt/examples/custom-layout.apt.vm | 57 +++++++++++++++++++ .../src/site/apt/index.apt | 2 + 12 files changed, 154 insertions(+), 15 deletions(-) create mode 100644 spring-boot-tools/spring-boot-maven-plugin/src/site/apt/examples/custom-layout.apt.vm 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 6b662acd1ab4..55449e04e6f0 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 9b250d9cbc42..d84bb2134c73 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.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.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 d5a05dd0946f..929ef645dcb3 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 @@ */ 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 @@ private long getNestedLibraryTime(File file) { /** * 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 38681e107eeb..09f7ded6be26 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 66c5738f8611..9b21eceeb3e0 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 @@ /** * 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 ff101007fed9..6901a2111773 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 @@ /** * 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 3cebdc7a05c5..00315e0e9cc7 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 @@ */ public final class Layouts { + /** + * Default value for {@link Layout#getLoaderJarPath()}. + */ + public static final String DEFAULT_LOADER_JAR = "META-INF/loader/spring-boot-loader.jar"; + private Layouts() { } @@ -87,6 +92,11 @@ public boolean isExecutable() { return true; } + @Override + public String getLoaderJarPath() { + return DEFAULT_LOADER_JAR; + } + } /** @@ -116,6 +126,11 @@ public boolean isExecutable() { return false; } + @Override + public String getLoaderJarPath() { + return DEFAULT_LOADER_JAR; + } + } /** @@ -154,6 +169,11 @@ public boolean isExecutable() { return true; } + @Override + public String getLoaderJarPath() { + return DEFAULT_LOADER_JAR; + } + } /** @@ -188,6 +208,11 @@ public boolean isExecutable() { 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 988e4ba0d487..ec651463692d 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 void library(Library library) throws IOException { } 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 5fc5135419b1..3792529f22d8 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 Layout getLayout() { @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 2e07cba4a0ad..42de1700b4b2 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 @@ private Repackager getRepackager(File source) { 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 000000000000..031e29ff71f3 --- /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 dcf50844057e..cb767f9e6415 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}} From 3fb135d3bf96afe44aafd69792a64fefe77aa192 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Wed, 9 Nov 2016 10:45:47 +0000 Subject: [PATCH 4/6] Refactor to make LayoutType a true enum again This makes the autocompletion for pom.xml and (maybe) build.gradle easier for most users, but at the cost of a slightly odd API for deriving Layout from LayoutType. --- .../src/main/asciidoc/build-tool-plugins.adoc | 17 +++-- .../gradle/SpringBootPluginExtension.java | 29 ++++++-- .../boot/gradle/repackage/RepackageTask.java | 4 +- .../loader/tools/DefaultLayoutFactory.java | 44 ++++++++++++ .../boot/loader/tools/LayoutFactory.java | 4 +- .../boot/loader/tools/LayoutType.java | 67 +++++++------------ .../boot/loader/tools/Layouts.java | 14 +--- .../boot/loader/tools/LayoutTypeTests.java | 14 ++-- .../boot/maven/RepackageMojo.java | 23 +++++-- .../site/apt/examples/custom-layout.apt.vm | 7 +- 10 files changed, 132 insertions(+), 91 deletions(-) create mode 100644 spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/DefaultLayoutFactory.java 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 55449e04e6f0..221b9d4e638d 100644 --- a/spring-boot-docs/src/main/asciidoc/build-tool-plugins.adoc +++ b/spring-boot-docs/src/main/asciidoc/build-tool-plugins.adoc @@ -480,9 +480,12 @@ The following configuration options are available: |The name of the custom configuration. |`layout` -|The type of archive, corresponding to how the dependencies are laid out inside - (defaults to a guess based on the archive type). See - <>. +|The `LayoutType` of archive, corresponding to how the dependencies are laid out inside + (defaults to a guess based on the archive type). + +|`layoutFactory` +|A factory for the actual `Layout` derived from the `LayoutType`. See +<>. |`requiresUnpack` |A list of dependencies (in the form "`groupId:artifactId`" that must be unpacked from @@ -534,9 +537,9 @@ loader should be included or not. The following layouts are available: ==== 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 +Any library which defines one or more `LayoutFactory` implementations 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"] ---- @@ -556,7 +559,7 @@ buildscript { } springBoot { - layout = 'CUSTOM' + layoutFactory = new com.example.CustomLayoutFactory() } ---- 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 d84bb2134c73..d18ac6dbeb50 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,7 +26,9 @@ import org.gradle.api.plugins.JavaPlugin; import org.springframework.boot.gradle.buildinfo.BuildInfo; +import org.springframework.boot.loader.tools.DefaultLayoutFactory; import org.springframework.boot.loader.tools.Layout; +import org.springframework.boot.loader.tools.LayoutFactory; import org.springframework.boot.loader.tools.LayoutType; /** @@ -89,7 +91,12 @@ public class SpringBootPluginExtension { * the MANIFEST.MF 'Main-Class' to be PropertiesLauncher. Gradle will coerce literal * String values to the correct type. */ - String layout; + LayoutType layout; + + /** + * The layout factory to use to convert a layout type into an actual layout. + */ + LayoutFactory layoutFactory = new DefaultLayoutFactory(); /** * Libraries that must be unpacked from fat jars in order to run. Use Strings in the @@ -143,10 +150,14 @@ public SpringBootPluginExtension(Project project) { /** * Convenience method for use in a custom task. + * @param file the file to use to guess the layout if necessary * @return the Layout to use or null if not explicitly set */ - public Layout convertLayout() { - return (this.layout == null ? null : LayoutType.layout(this.layout)); + public Layout convertLayout(File file) { + if (this.layout == null) { + this.layout = LayoutType.forFile(file); + } + return this.layoutFactory.getLayout(this.layout); } public String getMainClass() { @@ -189,14 +200,22 @@ public void setBackupSource(boolean backupSource) { this.backupSource = backupSource; } - public String getLayout() { + public LayoutType getLayout() { return this.layout; } - public void setLayout(String layout) { + public void setLayout(LayoutType layout) { 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 92d99dbf3514..218cc999a745 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 @@ -217,9 +217,7 @@ private void repackage(File file) { } Repackager repackager = new LoggingRepackager(file); setMainClass(repackager); - if (this.extension.convertLayout() != null) { - repackager.setLayout(this.extension.convertLayout()); - } + repackager.setLayout(this.extension.convertLayout(file)); repackager.setBackupSource(this.extension.isBackupSource()); try { LaunchScript launchScript = getLaunchScript(); 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 000000000000..7ff9c9ec6b6c --- /dev/null +++ b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/DefaultLayoutFactory.java @@ -0,0 +1,44 @@ +/* + * Copyright 2012-2015 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; + +/** + * Default implementation of layout factory that looks up the enum value and maps it to a + * layout from {@link Layouts}. + * + * @author Dave Syer + * + */ +public class DefaultLayoutFactory implements LayoutFactory { + + @Override + public Layout getLayout(LayoutType type) { + switch (type) { + case JAR: + return new Layouts.Jar(); + case WAR: + return new Layouts.War(); + case ZIP: + return new Layouts.Expanded(); + case MODULE: + return new Layouts.Module(); + default: + return new Layouts.None(); + } + } + +} 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 9b21eceeb3e0..782f2f916b9e 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 @@ -24,8 +24,6 @@ */ public interface LayoutFactory { - Layout getLayout(); - - String getName(); + Layout getLayout(LayoutType type); } 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 6901a2111773..41131f188538 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 @@ -16,11 +16,7 @@ package org.springframework.boot.loader.tools; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.springframework.core.io.support.SpringFactoriesLoader; +import java.io.File; /** * Archive layout types. @@ -32,70 +28,53 @@ public enum LayoutType { /** * Jar Layout. */ - JAR(new Layouts.Jar()), + JAR, /** * War Layout. */ - WAR(new Layouts.War()), + WAR, /** * Zip Layout. */ - ZIP(new Layouts.Expanded()), + ZIP, /** * Dir Layout. */ - DIR(new Layouts.Expanded()), + DIR, /** * Module Layout. */ - MODULE(new Layouts.Module()), + MODULE, /** * No Layout. */ - NONE(new Layouts.None()); - - private static Map customTypes; - - private final Layout layout; + NONE; - public Layout layout() { - return this.layout; - } - - LayoutType(Layout layout) { - this.layout = layout; - } - public static Layout layout(String value) { - try { - return valueOf(value).layout(); + /** + * Return a layout type for the given source file. + * @param file the source file + * @return a {@link Layout} + */ + public static LayoutType forFile(File file) { + if (file == null) { + throw new IllegalArgumentException("File must not be null"); } - catch (IllegalArgumentException e) { - if (customTypes == null) { - customTypes = new HashMap(); - lookupCustomTypes(); - } - Layout layout = customTypes.get(value); - if (layout == null) { - throw new IllegalArgumentException( - "Cannot resolve custom layout type: " + value); - } - return layout; + if (file.getName().toLowerCase().endsWith(".jar")) { + return JAR; } - } - - private static void lookupCustomTypes() { - ClassLoader classLoader = LayoutType.class.getClassLoader(); - List factories = SpringFactoriesLoader - .loadFactories(LayoutFactory.class, classLoader); - for (LayoutFactory factory : factories) { - customTypes.put(factory.getName(), factory.getLayout()); + if (file.getName().toLowerCase().endsWith(".war")) { + return WAR; + } + if (file.isDirectory() || file.getName().toLowerCase().endsWith(".zip")) { + return ZIP; } + throw new IllegalArgumentException("Unable to deduce layout for '" + file + "'"); } } 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 00315e0e9cc7..c9b4f42d1251 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 @@ -47,19 +47,7 @@ private Layouts() { * @return a {@link Layout} */ public static Layout forFile(File file) { - if (file == null) { - throw new IllegalArgumentException("File must not be null"); - } - if (file.getName().toLowerCase().endsWith(".jar")) { - return new Jar(); - } - if (file.getName().toLowerCase().endsWith(".war")) { - return new War(); - } - if (file.isDirectory() || file.getName().toLowerCase().endsWith(".zip")) { - return new Expanded(); - } - throw new IllegalStateException("Unable to deduce layout for '" + file + "'"); + return new DefaultLayoutFactory().getLayout(LayoutType.forFile(file)); } /** 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 3792529f22d8..767d14833944 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 @@ -26,28 +26,26 @@ */ public class LayoutTypeTests { + private LayoutFactory factory = new DefaultLayoutFactory(); + @Test public void standardType() { - assertThat(LayoutType.layout("DIR")) - .isEqualTo(LayoutType.valueOf("DIR").layout()); + assertThat(this.factory.getLayout(LayoutType.DIR)).isNotNull(); } @Test public void customType() { - assertThat(LayoutType.layout("CUSTOM")).isNotNull(); + this.factory = new TestLayoutFactory(); + assertThat(this.factory.getLayout(LayoutType.MODULE)).isNotNull(); } public static class TestLayoutFactory implements LayoutFactory { @Override - public Layout getLayout() { + public Layout getLayout(LayoutType type) { return new Layouts.Jar(); } - @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 42de1700b4b2..3515035a84d2 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 @@ -41,7 +41,9 @@ import org.apache.maven.shared.artifact.filter.collection.ScopeFilter; import org.springframework.boot.loader.tools.DefaultLaunchScript; +import org.springframework.boot.loader.tools.DefaultLayoutFactory; import org.springframework.boot.loader.tools.LaunchScript; +import org.springframework.boot.loader.tools.LayoutFactory; import org.springframework.boot.loader.tools.LayoutType; import org.springframework.boot.loader.tools.Libraries; import org.springframework.boot.loader.tools.Repackager; @@ -130,7 +132,14 @@ public class RepackageMojo extends AbstractDependencyFilterMojo { * @since 1.0 */ @Parameter - private String layout; + private LayoutType layout; + + /** + * The type of the layout factory that converts layout type to an actual layout. + * @since 1.0 + */ + @Parameter + private LayoutFactory layoutFactory = new DefaultLayoutFactory(); /** * A list of the libraries that must be unpacked from fat jars in order to run. @@ -225,9 +234,15 @@ private File getTargetFile() { private Repackager getRepackager(File source) { Repackager repackager = new LoggingRepackager(source, getLog()); repackager.setMainClass(this.mainClass); - if (this.layout != null) { - getLog().info("Layout: " + this.layout); - repackager.setLayout(LayoutType.layout(this.layout)); + if (this.layout == null) { + this.layout = LayoutType.forFile(source); + } + getLog().info("Layout: " + this.layout); + try { + repackager.setLayout(this.layoutFactory.getLayout(this.layout)); + } + catch (Exception e) { + throw new IllegalStateException("Cannot create layout", e); } 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 index 031e29ff71f3..a5dd5650711f 100644 --- 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 @@ -7,7 +7,7 @@ ----- Spring Boot repackages the jar file for this project using a custom - layout defined in the additional jar file, provided as a dependency + layout factory defined in the additional jar file, provided as a dependency to the build plugin: --- @@ -27,7 +27,7 @@ repackage - CUSTOM + @@ -49,8 +49,7 @@ --- The layout is provided as an implementation of <> - (from spring-boot-loader-tools) listed in - <> inside the <> jar. + (from spring-boot-loader-tools) explicitly specified in the pom. From aa514a43f873558cd5cbffcefcec7c018d2ac5f1 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Wed, 9 Nov 2016 11:09:31 +0000 Subject: [PATCH 5/6] Locate a default LayoutFactory in spring.factories if possible As a courtesy to the user, if there is only one LayoutFactory defined in spring.factories, then there is no need to explicitly configure it. --- .../src/main/asciidoc/build-tool-plugins.adoc | 3 +++ .../gradle/SpringBootPluginExtension.java | 4 ++-- .../boot/loader/tools/Layouts.java | 20 ++++++++++++++++++- .../test/resources/META-INF/spring.factories | 2 -- .../boot/maven/RepackageMojo.java | 4 ++-- .../site/apt/examples/custom-layout.apt.vm | 6 ++++-- 6 files changed, 30 insertions(+), 9 deletions(-) delete mode 100644 spring-boot-tools/spring-boot-loader-tools/src/test/resources/META-INF/spring.factories 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 221b9d4e638d..54ea4b0a4ec4 100644 --- a/spring-boot-docs/src/main/asciidoc/build-tool-plugins.adoc +++ b/spring-boot-docs/src/main/asciidoc/build-tool-plugins.adoc @@ -563,6 +563,9 @@ springBoot { } ---- +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. [[build-tool-plugins-understanding-the-gradle-plugin]] === Understanding how the Gradle plugin works 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 d18ac6dbeb50..bf676dc706f1 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,10 +26,10 @@ import org.gradle.api.plugins.JavaPlugin; import org.springframework.boot.gradle.buildinfo.BuildInfo; -import org.springframework.boot.loader.tools.DefaultLayoutFactory; import org.springframework.boot.loader.tools.Layout; import org.springframework.boot.loader.tools.LayoutFactory; import org.springframework.boot.loader.tools.LayoutType; +import org.springframework.boot.loader.tools.Layouts; /** * Gradle DSL Extension for 'Spring Boot'. Most of the time Spring Boot can guess the @@ -96,7 +96,7 @@ public class SpringBootPluginExtension { /** * The layout factory to use to convert a layout type into an actual layout. */ - LayoutFactory layoutFactory = new DefaultLayoutFactory(); + LayoutFactory layoutFactory = Layouts.getDefaultLayoutFactory(); /** * Libraries that must be unpacked from fat jars in order to run. Use Strings in the 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 c9b4f42d1251..62fd8fee016e 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 @@ -21,9 +21,12 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; +import org.springframework.core.io.support.SpringFactoriesLoader; + /** * Common {@link Layout}s. * @@ -47,7 +50,22 @@ private Layouts() { * @return a {@link Layout} */ public static Layout forFile(File file) { - return new DefaultLayoutFactory().getLayout(LayoutType.forFile(file)); + return Layouts.getDefaultLayoutFactory().getLayout(LayoutType.forFile(file)); + } + + /** + * Gets a default layout factory, trying first to find a unique one in spring + * factories, and then falling back to {@link DefaultLayoutFactory} if there isn't + * one. + * @return the default layout factory + */ + public static LayoutFactory getDefaultLayoutFactory() { + List factories = SpringFactoriesLoader + .loadFactories(LayoutFactory.class, null); + if (factories.size() == 1) { + return factories.get(0); + } + return new DefaultLayoutFactory(); } /** diff --git a/spring-boot-tools/spring-boot-loader-tools/src/test/resources/META-INF/spring.factories b/spring-boot-tools/spring-boot-loader-tools/src/test/resources/META-INF/spring.factories deleted file mode 100644 index 37eac70aa358..000000000000 --- a/spring-boot-tools/spring-boot-loader-tools/src/test/resources/META-INF/spring.factories +++ /dev/null @@ -1,2 +0,0 @@ -org.springframework.boot.loader.tools.LayoutFactory=\ -org.springframework.boot.loader.tools.LayoutTypeTests.TestLayoutFactory \ No newline at end of file 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 3515035a84d2..c32be181557a 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 @@ -41,10 +41,10 @@ import org.apache.maven.shared.artifact.filter.collection.ScopeFilter; import org.springframework.boot.loader.tools.DefaultLaunchScript; -import org.springframework.boot.loader.tools.DefaultLayoutFactory; import org.springframework.boot.loader.tools.LaunchScript; import org.springframework.boot.loader.tools.LayoutFactory; import org.springframework.boot.loader.tools.LayoutType; +import org.springframework.boot.loader.tools.Layouts; import org.springframework.boot.loader.tools.Libraries; import org.springframework.boot.loader.tools.Repackager; @@ -139,7 +139,7 @@ public class RepackageMojo extends AbstractDependencyFilterMojo { * @since 1.0 */ @Parameter - private LayoutFactory layoutFactory = new DefaultLayoutFactory(); + private LayoutFactory layoutFactory = Layouts.getDefaultLayoutFactory(); /** * A list of the libraries that must be unpacked from fat jars in order to run. 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 index a5dd5650711f..01b0ea0b37d6 100644 --- 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 @@ -49,8 +49,10 @@ --- The layout is provided as an implementation of <> - (from spring-boot-loader-tools) explicitly specified in the pom. - + (from spring-boot-loader-tools) explicitly specified in the pom. If + there is only one custom `LayoutFactory` on the plugin classpath and + it is listed in `META-INF/spring.factories` then it is unnecessary + to explicitly set it in the plugin configuration. From 24ec410f53ec5f85139ddf11a8037da0fd0e1689 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Mon, 14 Nov 2016 12:10:16 +0000 Subject: [PATCH 6/6] Simplify contract of LayoutFactory It's simpler if the build tools just use a factory if one is provided otherwise derive the layout from its type. --- .../src/main/asciidoc/build-tool-plugins.adoc | 6 +-- .../gradle/SpringBootPluginExtension.java | 13 +++-- .../loader/tools/DefaultLayoutFactory.java | 44 ---------------- .../boot/loader/tools/LayoutFactory.java | 2 +- .../boot/loader/tools/Layouts.java | 24 ++++++++- .../boot/loader/tools/LayoutTypeTests.java | 51 ------------------- .../boot/maven/RepackageMojo.java | 16 ++++-- 7 files changed, 48 insertions(+), 108 deletions(-) delete mode 100644 spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/DefaultLayoutFactory.java delete mode 100644 spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/LayoutTypeTests.java 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 54ea4b0a4ec4..116dc7d4d9f3 100644 --- a/spring-boot-docs/src/main/asciidoc/build-tool-plugins.adoc +++ b/spring-boot-docs/src/main/asciidoc/build-tool-plugins.adoc @@ -481,11 +481,11 @@ The following configuration options are available: |`layout` |The `LayoutType` of archive, corresponding to how the dependencies are laid out inside - (defaults to a guess based on the archive type). + (defaults to a guess based on the archive type). See +<>. |`layoutFactory` -|A factory for the actual `Layout` derived from the `LayoutType`. See -<>. +|A factory for the actual `Layout` derived from the `LayoutType`. If the factory is provided then the layout is ignored. |`requiresUnpack` |A list of dependencies (in the form "`groupId:artifactId`" that must be unpacked from 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 bf676dc706f1..fa32355ea2ed 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 @@ -154,10 +154,17 @@ public SpringBootPluginExtension(Project project) { * @return the Layout to use or null if not explicitly set */ public Layout convertLayout(File file) { - if (this.layout == null) { - this.layout = LayoutType.forFile(file); + Layout result; + if (this.layoutFactory != null) { + result = this.layoutFactory.getLayout(); } - return this.layoutFactory.getLayout(this.layout); + else if (this.layout == null) { + result = Layouts.forFile(file); + } + else { + result = Layouts.forType(this.layout); + } + return result; } public String getMainClass() { 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 deleted file mode 100644 index 7ff9c9ec6b6c..000000000000 --- a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/DefaultLayoutFactory.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2012-2015 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; - -/** - * Default implementation of layout factory that looks up the enum value and maps it to a - * layout from {@link Layouts}. - * - * @author Dave Syer - * - */ -public class DefaultLayoutFactory implements LayoutFactory { - - @Override - public Layout getLayout(LayoutType type) { - switch (type) { - case JAR: - return new Layouts.Jar(); - case WAR: - return new Layouts.War(); - case ZIP: - return new Layouts.Expanded(); - case MODULE: - return new Layouts.Module(); - default: - return new Layouts.None(); - } - } - -} 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 782f2f916b9e..09b892ddefa7 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 @@ -24,6 +24,6 @@ */ public interface LayoutFactory { - Layout getLayout(LayoutType type); + Layout getLayout(); } 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 62fd8fee016e..b13e6c67d699 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 @@ -50,7 +50,27 @@ private Layouts() { * @return a {@link Layout} */ public static Layout forFile(File file) { - return Layouts.getDefaultLayoutFactory().getLayout(LayoutType.forFile(file)); + return forType(LayoutType.forFile(file)); + } + + /** + * Return a layout for the given type. + * @param type the layout type + * @return a {@link Layout} + */ + public static Layout forType(LayoutType type) { + switch (type) { + case JAR: + return new Layouts.Jar(); + case WAR: + return new Layouts.War(); + case ZIP: + return new Layouts.Expanded(); + case MODULE: + return new Layouts.Module(); + default: + return new Layouts.None(); + } } /** @@ -65,7 +85,7 @@ public static LayoutFactory getDefaultLayoutFactory() { if (factories.size() == 1) { return factories.get(0); } - return new DefaultLayoutFactory(); + return null; } /** 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 deleted file mode 100644 index 767d14833944..000000000000 --- a/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/LayoutTypeTests.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2012-2015 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 org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Dave Syer - * - */ -public class LayoutTypeTests { - - private LayoutFactory factory = new DefaultLayoutFactory(); - - @Test - public void standardType() { - assertThat(this.factory.getLayout(LayoutType.DIR)).isNotNull(); - } - - @Test - public void customType() { - this.factory = new TestLayoutFactory(); - assertThat(this.factory.getLayout(LayoutType.MODULE)).isNotNull(); - } - - public static class TestLayoutFactory implements LayoutFactory { - - @Override - public Layout getLayout(LayoutType type) { - return new Layouts.Jar(); - } - - } - -} 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 c32be181557a..ba950166bb0d 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 @@ -42,6 +42,7 @@ 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.LayoutType; import org.springframework.boot.loader.tools.Layouts; @@ -234,12 +235,19 @@ private File getTargetFile() { private Repackager getRepackager(File source) { Repackager repackager = new LoggingRepackager(source, getLog()); repackager.setMainClass(this.mainClass); - if (this.layout == null) { - this.layout = LayoutType.forFile(source); + Layout forUse; + if (this.layoutFactory != null) { + forUse = this.layoutFactory.getLayout(); } - getLog().info("Layout: " + this.layout); + else if (this.layout == null) { + forUse = Layouts.forFile(source); + } + else { + forUse = Layouts.forType(this.layout); + } + getLog().info("Layout: " + forUse.getClass()); try { - repackager.setLayout(this.layoutFactory.getLayout(this.layout)); + repackager.setLayout(forUse); } catch (Exception e) { throw new IllegalStateException("Cannot create layout", e);