From 47cd5dd679ce10f95aaa795cdec7314189ab3c7c Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Tue, 15 Oct 2013 16:30:27 -0400 Subject: [PATCH] Tooling for PropertiesLauncher in JAR archives To use PropertiesLauncher instead of JarLauncher in an executable JAR we have provided tooling support. In Maven (using the starter parent to default some of the settings): org.springframework.boot spring-boot-maven-plugin ZIP in Gradle: apply plugin: "spring-boot" springBoot { layout = 'ZIP' } } [Fixes #58837492] [bs-330] Add tooling for PropertiesLauncher --- .../gradle/SpringBootPluginExtension.groovy | 46 +++++++++++++++- .../boot/gradle/task/Repackage.java | 6 ++- .../org/springframework/boot/git.properties | 10 ++-- .../boot/loader/tools/Layouts.java | 15 ++++++ .../org/springframework/boot/git.properties | 16 +++--- .../boot/loader/archive/Archive.java | 16 +++++- .../org/springframework/boot/git.properties | 16 +++--- .../src/it/prop/pom.xml | 53 +++++++++++++++++++ .../main/java/org/test/SampleApplication.java | 8 +++ .../src/it/prop/verify.groovy | 7 +++ .../boot/maven/RepackageMojo.java | 26 +++++++++ .../org/springframework/boot/git.properties | 16 +++--- .../springframework/boot/maven/Verify.java | 20 +++++++ 13 files changed, 220 insertions(+), 35 deletions(-) create mode 100644 spring-boot-tools/spring-boot-maven-plugin/src/it/prop/pom.xml create mode 100644 spring-boot-tools/spring-boot-maven-plugin/src/it/prop/src/main/java/org/test/SampleApplication.java create mode 100644 spring-boot-tools/spring-boot-maven-plugin/src/it/prop/verify.groovy 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 19afbb2455..cd36bc8592 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 @@ -16,13 +16,35 @@ package org.springframework.boot.gradle +import org.springframework.boot.loader.tools.Layout +import org.springframework.boot.loader.tools.Layouts + /** - * Gradle DSL Extension for 'Spring Boot'. + * 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 two of them. E.g. + * + *
+ *     apply plugin: "spring-boot"
+ *     springBoot {
+ *         mainClass = 'org.demo.Application'
+ *         layout = 'ZIP'
+ *     }
+ * 
* * @author Phillip Webb + * @author Dave Syer */ public class SpringBootPluginExtension { + static enum LayoutType { + JAR(new Layouts.Jar()), WAR(new Layouts.War()), ZIP(new Layouts.Expanded()), DIR(new Layouts.Expanded()); + Layout layout; + private LayoutType(Layout layout) { + this.layout = layout; + } + } + /** * The main class that should be run. If not specified the value from the * MANIFEST will be used, or if no manifest entry is the archive will be @@ -31,7 +53,8 @@ public class SpringBootPluginExtension { String mainClass /** - * The name of the provided configuration. If not specified 'providedRuntime' will + * The name of the ivy configuration name to treat as 'provided' (when packaging + * those dependencies in a separate path). If not specified 'providedRuntime' will * be used. */ String providedConfiguration @@ -40,4 +63,23 @@ public class SpringBootPluginExtension { * If the original source archive should be backed-up before being repackaged. */ boolean backupSource = true; + + /** + * The layout of the archive if it can't be derived from the file extension. + * Valid values are JAR, WAR, ZIP, DIR (for exploded zip file). ZIP and DIR + * are actually synonymous, and should be used if there is no MANIFEST.MF + * available, or if you want the MANIFEST.MF 'Main-Class' to be + * PropertiesLauncher. Gradle will coerce literal String values to the + * correct type. + */ + LayoutType 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 + } } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/task/Repackage.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/task/Repackage.java index 9b455ae077..521a936e18 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/task/Repackage.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/task/Repackage.java @@ -50,11 +50,13 @@ public class Repackage extends DefaultTask { if (file.exists()) { Repackager repackager = new Repackager(file); repackager.setMainClass(extension.getMainClass()); + if (extension.convertLayout() != null) { + repackager.setLayout(extension.convertLayout()); + } repackager.setBackupSource(extension.isBackupSource()); try { repackager.repackage(libraries); - } - catch (IOException ex) { + } catch (IOException ex) { throw new IllegalStateException(ex.getMessage(), ex); } } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/resources/org/springframework/boot/git.properties b/spring-boot-tools/spring-boot-gradle-plugin/src/main/resources/org/springframework/boot/git.properties index 4c71ec6a6b..ec98f87d95 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/resources/org/springframework/boot/git.properties +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/resources/org/springframework/boot/git.properties @@ -1,13 +1,13 @@ #Generated by Git-Commit-Id-Plugin -#Tue Oct 15 11:07:38 EDT 2013 -git.commit.id.abbrev=d3fa609 +#Tue Oct 15 11:35:18 EDT 2013 +git.commit.id.abbrev=ea11daf git.commit.user.email=dsyer@gopivotal.com git.commit.message.full=Extend PropertiesLauncher to load nested archives\n\nPropertiesLauncher can now be used to run an executable jar, and by\ndefault it will pick up nested archives in lib/ (where the Boot\ntools puts them). User can provide loader.path (colon-separated)\nto change the nested path.\n\n[\#58837492] [bs-330] Add tooling for PropertiesLauncher\n -git.commit.id=d3fa60955b06fe78bbf0c914928d794661aca312 +git.commit.id=ea11dafcbd951b3b23257585bce1f479ca9faa73 git.commit.message.short=Extend PropertiesLauncher to load nested archives git.commit.user.name=Dave Syer git.build.user.name=Dave Syer git.build.user.email=dsyer@gopivotal.com git.branch=master -git.commit.time=2013-10-15T10\:51\:03-0400 -git.build.time=2013-10-15T11\:07\:38-0400 +git.commit.time=2013-10-15T11\:08\:45-0400 +git.build.time=2013-10-15T11\:35\:18-0400 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 b3e2d3becf..32ac121f60 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 @@ -43,6 +43,9 @@ public class Layouts { 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 + "'"); } @@ -67,6 +70,18 @@ public class Layouts { } } + /** + * Executable expanded archive layout. + */ + public static class Expanded extends Jar { + + @Override + public String getLauncherClassName() { + return "org.springframework.boot.loader.PropertiesLauncher"; + } + + } + /** * Executable WAR layout. */ diff --git a/spring-boot-tools/spring-boot-loader-tools/src/main/resources/org/springframework/boot/git.properties b/spring-boot-tools/spring-boot-loader-tools/src/main/resources/org/springframework/boot/git.properties index c284d0a7ad..7cea85057f 100644 --- a/spring-boot-tools/spring-boot-loader-tools/src/main/resources/org/springframework/boot/git.properties +++ b/spring-boot-tools/spring-boot-loader-tools/src/main/resources/org/springframework/boot/git.properties @@ -1,13 +1,13 @@ #Generated by Git-Commit-Id-Plugin -#Tue Oct 15 11:07:33 EDT 2013 -git.commit.id.abbrev=d3fa609 +#Wed Oct 16 08:14:33 EDT 2013 +git.commit.id.abbrev=a7ba9ba git.commit.user.email=dsyer@gopivotal.com -git.commit.message.full=Extend PropertiesLauncher to load nested archives\n\nPropertiesLauncher can now be used to run an executable jar, and by\ndefault it will pick up nested archives in lib/ (where the Boot\ntools puts them). User can provide loader.path (colon-separated)\nto change the nested path.\n\n[\#58837492] [bs-330] Add tooling for PropertiesLauncher\n -git.commit.id=d3fa60955b06fe78bbf0c914928d794661aca312 -git.commit.message.short=Extend PropertiesLauncher to load nested archives +git.commit.message.full=Tooling for PropertiesLauncher\n +git.commit.id=a7ba9ba5cd47a924f9c7668a772957fc05ffa058 +git.commit.message.short=Tooling for PropertiesLauncher git.commit.user.name=Dave Syer git.build.user.name=Dave Syer git.build.user.email=dsyer@gopivotal.com -git.branch=master -git.commit.time=2013-10-15T10\:51\:03-0400 -git.build.time=2013-10-15T11\:07\:33-0400 +git.branch=feature/proptool +git.commit.time=2013-10-15T16\:54\:14-0400 +git.build.time=2013-10-16T08\:14\:33-0400 diff --git a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/archive/Archive.java b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/archive/Archive.java index 8bc7acbcc3..fd28fe63c5 100644 --- a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/archive/Archive.java +++ b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/archive/Archive.java @@ -47,9 +47,21 @@ public abstract class Archive { * @throws Exception */ public String getMainClass() throws Exception { - String mainClass = getManifest().getMainAttributes().getValue("Start-Class"); + Manifest manifest = getManifest(); + String mainClass = null; + if (manifest != null) { + mainClass = manifest.getMainAttributes().getValue("Start-Class"); + } if (mainClass == null) { - throw new IllegalStateException("No 'Start-Class' manifest entry specified"); + String url = "UNKNOWN"; + try { + url = getUrl().toString(); + } + catch (Exception e) { + // ignore + } + throw new IllegalStateException( + "No 'Start-Class' manifest entry specified in " + url); } return mainClass; } diff --git a/spring-boot-tools/spring-boot-loader/src/main/resources/org/springframework/boot/git.properties b/spring-boot-tools/spring-boot-loader/src/main/resources/org/springframework/boot/git.properties index 35fc5e979a..10e364fba2 100644 --- a/spring-boot-tools/spring-boot-loader/src/main/resources/org/springframework/boot/git.properties +++ b/spring-boot-tools/spring-boot-loader/src/main/resources/org/springframework/boot/git.properties @@ -1,13 +1,13 @@ #Generated by Git-Commit-Id-Plugin -#Tue Oct 15 11:07:32 EDT 2013 -git.commit.id.abbrev=d3fa609 +#Wed Oct 16 08:17:37 EDT 2013 +git.commit.id.abbrev=a7ba9ba git.commit.user.email=dsyer@gopivotal.com -git.commit.message.full=Extend PropertiesLauncher to load nested archives\n\nPropertiesLauncher can now be used to run an executable jar, and by\ndefault it will pick up nested archives in lib/ (where the Boot\ntools puts them). User can provide loader.path (colon-separated)\nto change the nested path.\n\n[\#58837492] [bs-330] Add tooling for PropertiesLauncher\n -git.commit.id=d3fa60955b06fe78bbf0c914928d794661aca312 -git.commit.message.short=Extend PropertiesLauncher to load nested archives +git.commit.message.full=Tooling for PropertiesLauncher\n +git.commit.id=a7ba9ba5cd47a924f9c7668a772957fc05ffa058 +git.commit.message.short=Tooling for PropertiesLauncher git.commit.user.name=Dave Syer git.build.user.name=Dave Syer git.build.user.email=dsyer@gopivotal.com -git.branch=master -git.commit.time=2013-10-15T10\:51\:03-0400 -git.build.time=2013-10-15T11\:07\:32-0400 +git.branch=feature/proptool +git.commit.time=2013-10-15T16\:54\:14-0400 +git.build.time=2013-10-16T08\:17\:37-0400 diff --git a/spring-boot-tools/spring-boot-maven-plugin/src/it/prop/pom.xml b/spring-boot-tools/spring-boot-maven-plugin/src/it/prop/pom.xml new file mode 100644 index 0000000000..56b0b7cd6c --- /dev/null +++ b/spring-boot-tools/spring-boot-maven-plugin/src/it/prop/pom.xml @@ -0,0 +1,53 @@ + + + 4.0.0 + org.springframework.boot.maven.it + jar + 0.0.1.BUILD-SNAPSHOT + jar + + + + @project.groupId@ + @project.artifactId@ + @project.version@ + + + + repackage + + + ZIP + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + + Foo + + + + + + + + + org.springframework + spring-context + 3.2.3.RELEASE + + + javax.servlet + javax.servlet-api + 3.0.1 + provided + + + diff --git a/spring-boot-tools/spring-boot-maven-plugin/src/it/prop/src/main/java/org/test/SampleApplication.java b/spring-boot-tools/spring-boot-maven-plugin/src/it/prop/src/main/java/org/test/SampleApplication.java new file mode 100644 index 0000000000..0b3b431677 --- /dev/null +++ b/spring-boot-tools/spring-boot-maven-plugin/src/it/prop/src/main/java/org/test/SampleApplication.java @@ -0,0 +1,8 @@ +package org.test; + +public class SampleApplication { + + public static void main(String[] args) { + } + +} diff --git a/spring-boot-tools/spring-boot-maven-plugin/src/it/prop/verify.groovy b/spring-boot-tools/spring-boot-maven-plugin/src/it/prop/verify.groovy new file mode 100644 index 0000000000..c001794627 --- /dev/null +++ b/spring-boot-tools/spring-boot-maven-plugin/src/it/prop/verify.groovy @@ -0,0 +1,7 @@ +import java.io.*; +import org.springframework.boot.maven.*; + +Verify.verifyZip( + new File( basedir, "target/jar-0.0.1.BUILD-SNAPSHOT.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 a5770f1649..aa020b47c4 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 @@ -29,6 +29,8 @@ import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; import org.apache.maven.project.MavenProject; import org.apache.maven.project.MavenProjectHelper; +import org.springframework.boot.loader.tools.Layout; +import org.springframework.boot.loader.tools.Layouts; import org.springframework.boot.loader.tools.Libraries; import org.springframework.boot.loader.tools.Repackager; @@ -81,12 +83,22 @@ public class RepackageMojo extends AbstractMojo { @Parameter private String mainClass; + /** + * The layout to use (JAR, WAR, ZIP, DIR) in case it cannot be inferred. + */ + @Parameter + private LayoutType layout; + @Override public void execute() throws MojoExecutionException, MojoFailureException { File source = this.project.getArtifact().getFile(); File target = getTargetFile(); Repackager repackager = new Repackager(source); repackager.setMainClass(this.mainClass); + if (this.layout != null) { + getLog().info("Layout: " + this.layout); + repackager.setLayout(this.layout.layout()); + } Libraries libraries = new ArtifactsLibraries(this.project.getArtifacts()); try { repackager.repackage(target, libraries); @@ -112,4 +124,18 @@ public class RepackageMojo extends AbstractMojo { + this.project.getPackaging()); } + public static enum LayoutType { + JAR(new Layouts.Jar()), WAR(new Layouts.War()), ZIP(new Layouts.Expanded()), DIR( + new Layouts.Expanded()); + private Layout layout; + + public Layout layout() { + return this.layout; + } + + private LayoutType(Layout layout) { + this.layout = layout; + } + } + } diff --git a/spring-boot-tools/spring-boot-maven-plugin/src/main/resources/org/springframework/boot/git.properties b/spring-boot-tools/spring-boot-maven-plugin/src/main/resources/org/springframework/boot/git.properties index 1bd00a35e8..6b11be6bc0 100644 --- a/spring-boot-tools/spring-boot-maven-plugin/src/main/resources/org/springframework/boot/git.properties +++ b/spring-boot-tools/spring-boot-maven-plugin/src/main/resources/org/springframework/boot/git.properties @@ -1,13 +1,13 @@ #Generated by Git-Commit-Id-Plugin -#Tue Oct 15 11:07:34 EDT 2013 -git.commit.id.abbrev=d3fa609 +#Wed Oct 16 08:42:05 EDT 2013 +git.commit.id.abbrev=a7ba9ba git.commit.user.email=dsyer@gopivotal.com -git.commit.message.full=Extend PropertiesLauncher to load nested archives\n\nPropertiesLauncher can now be used to run an executable jar, and by\ndefault it will pick up nested archives in lib/ (where the Boot\ntools puts them). User can provide loader.path (colon-separated)\nto change the nested path.\n\n[\#58837492] [bs-330] Add tooling for PropertiesLauncher\n -git.commit.id=d3fa60955b06fe78bbf0c914928d794661aca312 -git.commit.message.short=Extend PropertiesLauncher to load nested archives +git.commit.message.full=Tooling for PropertiesLauncher\n +git.commit.id=a7ba9ba5cd47a924f9c7668a772957fc05ffa058 +git.commit.message.short=Tooling for PropertiesLauncher git.commit.user.name=Dave Syer git.build.user.name=Dave Syer git.build.user.email=dsyer@gopivotal.com -git.branch=master -git.commit.time=2013-10-15T10\:51\:03-0400 -git.build.time=2013-10-15T11\:07\:34-0400 +git.branch=feature/proptool +git.commit.time=2013-10-15T16\:54\:14-0400 +git.build.time=2013-10-16T08\:42\:05-0400 diff --git a/spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/Verify.java b/spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/Verify.java index 2e71889855..dd7e879cb2 100644 --- a/spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/Verify.java +++ b/spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/Verify.java @@ -46,6 +46,10 @@ public class Verify { new WarArchiveVerification(file).verify(); } + public static void verifyZip(File file) throws Exception { + new ZipArchiveVerification(file).verify(); + } + private static abstract class AbstractArchiveVerification { private File file; @@ -154,4 +158,20 @@ public class Verify { } } + private static class ZipArchiveVerification extends AbstractArchiveVerification { + + public ZipArchiveVerification(File file) { + super(file); + } + + @Override + protected void verifyManifest(Manifest manifest) throws Exception { + assertEquals("org.springframework.boot.loader.PropertiesLauncher", manifest + .getMainAttributes().getValue("Main-Class")); + assertEquals("org.test.SampleApplication", manifest.getMainAttributes() + .getValue("Start-Class")); + assertEquals("Foo", manifest.getMainAttributes().getValue("Not-Used")); + } + } + }