diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/SpringBootPluginExtension.groovy b/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/SpringBootPluginExtension.groovy index a54ecc48ba83..8657cfb1a142 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/SpringBootPluginExtension.groovy +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/SpringBootPluginExtension.groovy @@ -41,26 +41,10 @@ import org.springframework.boot.loader.tools.Layouts */ public class SpringBootPluginExtension { - 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; - - private LayoutType(Layout layout) { - this.layout = layout; - } - } + /** + * Launcher class, replaces the default specified by layout. Optional. + */ + String launcherClass; /** * The main class that should be run. Instead of setting this explicitly you can use the @@ -102,14 +86,14 @@ public class SpringBootPluginExtension { * PropertiesLauncher. Gradle will coerce literal String values to the * correct type. */ - LayoutType layout; + String layout; /** * Convenience method for use in a custom task. * @return the Layout to use or null if not explicitly set */ Layout convertLayout() { - (layout == null ? null : layout.layout) + Layouts.resolve(layout) } /** diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/repackage/RepackageTask.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/repackage/RepackageTask.java index af1b1b9b6d61..15a5fd460dfb 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/repackage/RepackageTask.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/repackage/RepackageTask.java @@ -32,6 +32,7 @@ import org.springframework.boot.gradle.SpringBootPluginExtension; 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.Repackager; import org.springframework.util.FileCopyUtils; @@ -173,9 +174,8 @@ private void repackage(File file) { } Repackager repackager = new LoggingRepackager(file); setMainClass(repackager); - if (this.extension.convertLayout() != null) { - repackager.setLayout(this.extension.convertLayout()); - } + repackager.setLauncherClass(this.extension.getLauncherClass()); + repackager.setLayout(this.extension.convertLayout()); 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/Layouts.java b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Layouts.java index ed1a5cb1252e..37a37e435d5c 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 @@ -16,6 +16,8 @@ package org.springframework.boot.loader.tools; +import org.springframework.boot.loader.tools.layout.BaseLayout; + import java.io.File; import java.util.Arrays; import java.util.Collections; @@ -33,6 +35,49 @@ */ public class Layouts { + static private Class defaultLayout = Layouts.Jar.class; + + static private Map> map = new HashMap>() {{ + for (Class cls : Layouts.class.getClasses()) { + if (Layout.class.isAssignableFrom(cls)) { + put(toSimpleLayoutName(cls), (Class) cls); + } + } + }}; + + /** + * Resolves and instantiates named layout. + * If name is undefined, default layout is returned (JAR). + * If given name refers to one of the layout aliases (JAR, ZIP, etc), it is returned. + * Otherwise, assume layout name is an actual class name, and try to load it. + * @param name + * @return + * @throws RuntimeException + */ + static public Layout resolve(String name) throws RuntimeException { + try { + if (name == null) { return defaultLayout.newInstance(); } + + Class cls = null; + if (cls == null) { cls = map.get(name); } + if (cls == null) { cls = Class.forName(name); } + + return (Layout) cls.newInstance(); + } + catch (Exception e) { + throw new RuntimeException(String.format( + "Cannot resolve layout `%s`. " + + "Provide a fully qualified name of the class implementing Layout interface, " + + "or one of the following: %s", + name, map.keySet() + ), e); + } + } + + static private String toSimpleLayoutName(Class cls) { + return cls.getSimpleName().toUpperCase(); + } + /** * Return the a layout for the given source file. * @param file the source file @@ -90,9 +135,12 @@ public static class Expanded extends Jar { public String getLauncherClassName() { return "org.springframework.boot.loader.PropertiesLauncher"; } - } + public static class Zip extends Expanded {} + + public static class Dir extends Expanded {} + /** * No layout. */ @@ -110,6 +158,11 @@ public boolean isExecutable() { } + /** + * This is what NONE should be. + */ + public static class Null extends BaseLayout {} + /** * Executable WAR layout. */ 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 0d3acaf16ef8..d6cc723697f8 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 @@ -44,6 +44,8 @@ public class Repackager { private static final byte[] ZIP_FILE_HEADER = new byte[] { 'P', 'K', 3, 4 }; + private String launcherClass; + private String mainClass; private boolean backupSource = true; @@ -60,6 +62,10 @@ public Repackager(File source) { this.layout = Layouts.forFile(source); } + public void setLauncherClass(String launcherClass) { + this.launcherClass = launcherClass; + } + /** * Sets the main class that should be run. If not specified the value from the * MANIFEST will be used, or if no manifest entry is found the archive will be @@ -258,7 +264,9 @@ private Manifest buildManifest(JarFile source) throws IOException { if (startClass == null) { startClass = findMainMethod(source); } - String launcherClassName = this.layout.getLauncherClassName(); + String launcherClassName = (launcherClass != null) + ? launcherClass + : layout.getLauncherClassName(); if (launcherClassName != null) { manifest.getMainAttributes() .putValue(MAIN_CLASS_ATTRIBUTE, launcherClassName); diff --git a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/layout/BaseLayout.java b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/layout/BaseLayout.java new file mode 100644 index 000000000000..bf530dcc2506 --- /dev/null +++ b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/layout/BaseLayout.java @@ -0,0 +1,29 @@ +package org.springframework.boot.loader.tools.layout; + +import org.springframework.boot.loader.tools.Layout; +import org.springframework.boot.loader.tools.LibraryScope; + +/** + * @author Patrik Beno + */ +public class BaseLayout implements Layout { + @Override + public String getLauncherClassName() { + return null; + } + + @Override + public String getLibraryDestination(String libraryName, LibraryScope scope) { + return null; + } + + @Override + public String getClassesLocation() { + return ""; + } + + @Override + public boolean isExecutable() { + return false; + } +} 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 8199e5f754a3..2b03ccbeceb2 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 @@ -103,6 +103,12 @@ public class RepackageMojo extends AbstractDependencyFilterMojo { @Parameter private String classifier; + /** + * Name of the launcher class; overrides the one specified by layout. + */ + @Parameter + private String launcherClass; + /** * The name of the main class. If not specified the first compiled class found that * contains a 'main' method will be used. @@ -118,7 +124,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. @@ -182,10 +188,10 @@ protected String findMainMethod(JarFile source) throws IOException { } }; repackager.setMainClass(this.mainClass); - if (this.layout != null) { - getLog().info("Layout: " + this.layout); - repackager.setLayout(this.layout.layout()); - } + repackager.setLauncherClass(this.launcherClass); + + getLog().info("Layout: " + this.layout); + repackager.setLayout(Layouts.resolve(this.layout)); Set artifacts = filterDependencies(this.project.getArtifacts(), getFilters());