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 76fc73250cc..7be55a9ed17 100644 --- a/spring-boot-docs/src/main/asciidoc/build-tool-plugins.adoc +++ b/spring-boot-docs/src/main/asciidoc/build-tool-plugins.adoc @@ -404,6 +404,10 @@ The following configuration options are available: |=== |Name |Description +|`enabled` +|Boolean flag to switch the repackager off (sometimes useful if you +want the other Boot features but not this one) + |`mainClass` |The main class that should be run. If not specified the `mainClassName` project property will be used or, if the no `mainClassName` id defined the archive will be searched for a @@ -419,9 +423,10 @@ The following configuration options are available: the original jar as a dependency in another project, it's best to use an extension to define the executable archive. -|`withJarTask` -|The name of the `Jar` task (defaults to all) which is used to locate the archive to - repackage. +|`withJarTask` +|The name or value of the `Jar` task (defaults to all +tasks of type `Jar`) which is used to locate the archive to +repackage. |`customConfiguration` |The name of the custom configuration whuch is used to populate the nested lib directory diff --git a/spring-boot-docs/src/main/asciidoc/howto.adoc b/spring-boot-docs/src/main/asciidoc/howto.adoc index 206e95c30cc..73db77ade18 100644 --- a/spring-boot-docs/src/main/asciidoc/howto.adoc +++ b/spring-boot-docs/src/main/asciidoc/howto.adoc @@ -1579,6 +1579,122 @@ See the {spring-boot-maven-plugin-site}/usage.html[plugin documentation] for ful details. +[[howto-create-an-additional-executable-jar]] +=== Create an additional executable JAR + +If you want to use your project as a library jar for other projects to +depend on, and in addition have an executable (e.g. demo) version of +it, you will want to configure the build in a slightly different way. + +For Maven the normal JAR plugin and the Spring Boot plugin both have a +"classifier" configuration that you can add to create an additional JAR. +Example (using the Spring Boot Starter Parent to manage the plugin +versions and other configuration defaults): + +[source,xml,indent=0,subs="verbatim,quotes,attributes"] +---- + + + + org.springframework.boot + spring-boot-maven-plugin + + exec + + + + +---- + +Two jars are produced, the default one, and an executable one using +the Boot plugin with classifier "exec". + +For Gradle users the steps are similar. Example: + +[source,groovy,indent=0,subs="verbatim,attributes"] +---- +bootRepackage { + classifier = 'exec' +} +---- + +[[howto-create-a-nonexecutable-jar]] +=== Create a non-executable JAR with exclusions + +Often if you have an executable and a non-executable jar +as biuld products, the executable version will have additional +configuration files that ar enot needed in a library jar. E.g. the +`application.yml` configuration file might excluded from the +non-executable JAR. + +Here's how to do that in Maven + +[source,xml,indent=0,subs="verbatim,quotes,attributes"] +---- + + + + org.springframework.boot + spring-boot-maven-plugin + + exec + + + + maven-jar-plugin + + + exec + package + + jar + + + exec + + + + package + + jar + + + + true + + application.yml + + + + + + + +---- + +In Gradle you can create a new JAR archive with standard task DSL +features, and then have the `bootRepackage` task depend on that one +using its `withJarTask` property: + +[source,groovy,indent=0,subs="verbatim,attributes"] +---- +jar { + baseName = 'spring-boot-sample-profile' + version = '0.0.0' + excludes = ['**/application.yml'] +} + +task('execJar', type:Jar, dependsOn: 'jar') { + baseName = 'spring-boot-sample-profile' + version = '0.0.0' + classifier = 'exec' + from sourceSets.main.output +} + +bootRepackage { + withJarTask = tasks['execJar'] +} +---- [[howto-remote-debug-maven-run]] === Remote debug a Spring Boot application started with Maven diff --git a/spring-boot-samples/spring-boot-sample-profile/build.gradle b/spring-boot-samples/spring-boot-sample-profile/build.gradle new file mode 100644 index 00000000000..824c59f9296 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-profile/build.gradle @@ -0,0 +1,56 @@ +buildscript { + ext { + springBootVersion = '1.1.2.BUILD-SNAPSHOT' + } + repositories { + // NOTE: You should declare only repositories that you need here + mavenLocal() + mavenCentral() + maven { url "http://repo.spring.io/release" } + maven { url "http://repo.spring.io/milestone" } + maven { url "http://repo.spring.io/snapshot" } + } + dependencies { + classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") + } +} + +apply plugin: 'java' +apply plugin: 'eclipse' +apply plugin: 'idea' +apply plugin: 'spring-boot' + +jar { + baseName = 'spring-boot-sample-profile' + version = '0.0.0' + excludes = ['**/application.yml'] +} + +task('execJar', type:Jar, dependsOn: 'jar') { + baseName = 'spring-boot-sample-profile' + version = '0.0.0' + classifier = 'exec' + from sourceSets.main.output +} + +bootRepackage { + withJarTask = tasks['execJar'] +} + +repositories { + // NOTE: You should declare only repositories that you need here + mavenLocal() + mavenCentral() + maven { url "http://repo.spring.io/release" } + maven { url "http://repo.spring.io/milestone" } + maven { url "http://repo.spring.io/snapshot" } +} + +dependencies { + compile("org.springframework.boot:spring-boot-starter") + testCompile("org.springframework.boot:spring-boot-starter-test") +} + +task wrapper(type: Wrapper) { + gradleVersion = '1.6' +} diff --git a/spring-boot-samples/spring-boot-sample-profile/pom.xml b/spring-boot-samples/spring-boot-sample-profile/pom.xml index 96ee2b088eb..f1517faaff6 100644 --- a/spring-boot-samples/spring-boot-sample-profile/pom.xml +++ b/spring-boot-samples/spring-boot-sample-profile/pom.xml @@ -1,5 +1,6 @@ - + 4.0.0 @@ -34,6 +35,37 @@ org.springframework.boot spring-boot-maven-plugin + + exec + + + + maven-jar-plugin + + + exec + package + + jar + + + exec + + + + package + + jar + + + + true + + application.yml + + + + diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/repackage/RepackagePluginFeatures.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/repackage/RepackagePluginFeatures.java index f6c25fc63dd..e63879c3d68 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/repackage/RepackagePluginFeatures.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/repackage/RepackagePluginFeatures.java @@ -50,18 +50,22 @@ public class RepackagePluginFeatures implements PluginFeatures { + "archives so that they can be executed from the command " + "line using 'java -jar'"); task.setGroup(BasePlugin.BUILD_GROUP); - task.dependsOn(project.getConfigurations() - .getByName(Dependency.ARCHIVES_CONFIGURATION).getAllArtifacts() - .getBuildDependencies()); + task.dependsOn(project.getConfigurations().getByName( + Dependency.ARCHIVES_CONFIGURATION).getAllArtifacts().getBuildDependencies()); registerOutput(project, task); ensureTaskRunsOnAssembly(project, task); } private void registerOutput(Project project, final RepackageTask task) { project.afterEvaluate(new Action() { + @Override public void execute(Project project) { project.getTasks().withType(Jar.class, new OutputAction(task)); + Object withJar = task.getWithJarTask(); + if (withJar!=null) { + task.dependsOn(withJar); + } } }); } @@ -74,8 +78,8 @@ public class RepackagePluginFeatures implements PluginFeatures { * Register BootRepackage so that we can use task {@code foo(type: BootRepackage)}. */ private void registerRepackageTaskProperty(Project project) { - project.getExtensions().getExtraProperties() - .set("BootRepackage", RepackageTask.class); + project.getExtensions().getExtraProperties().set("BootRepackage", + RepackageTask.class); } private class OutputAction implements Action { @@ -115,8 +119,7 @@ public class RepackagePluginFeatures implements PluginFeatures { SpringBootPluginExtension.class); if (task.getClassifier() != null) { classifier = task.getClassifier(); - } - else if (extension.getClassifier() != null) { + } else if (extension.getClassifier() != null) { classifier = extension.getClassifier(); } if (classifier != null) { 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 3306abd7028..4dc14b8d867 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 @@ -53,10 +53,16 @@ public class RepackageTask extends DefaultTask { private File outputFile; + private boolean enabled = true; + public void setCustomConfiguration(String customConfiguration) { this.customConfiguration = customConfiguration; } + public Object getWithJarTask() { + return withJarTask; + } + public void setWithJarTask(Object withJarTask) { this.withJarTask = withJarTask; } @@ -77,6 +83,14 @@ public class RepackageTask extends DefaultTask { this.classifier = classifier; } + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + @TaskAction public void repackage() { Project project = getProject(); @@ -85,12 +99,13 @@ public class RepackageTask extends DefaultTask { ProjectLibraries libraries = getLibraries(); project.getTasks().withType(Jar.class, new RepackageAction(extension, libraries)); } - + public File[] getDependencies() { ProjectLibraries libraries = getLibraries(); final List files = new ArrayList(); try { libraries.doWithLibraries(new LibraryCallback() { + @Override public void library(File file, LibraryScope scope) throws IOException { files.add(file); @@ -112,8 +127,7 @@ public class RepackageTask extends DefaultTask { } if (this.customConfiguration != null) { libraries.setCustomConfigurationName(this.customConfiguration); - } - else if (extension.getCustomConfiguration() != null) { + } else if (extension.getCustomConfiguration() != null) { libraries.setCustomConfigurationName(extension.getCustomConfiguration()); } return libraries; @@ -133,22 +147,30 @@ public class RepackageTask extends DefaultTask { @Override public void execute(Jar archive) { + if (!enabled) { + getLogger().info("Repackage disabled"); + return; + } // if withJarTask is set, compare tasks and bail out if we didn't match if (RepackageTask.this.withJarTask != null - && !archive.equals(RepackageTask.this.withJarTask)) { + && !(archive.equals(RepackageTask.this.withJarTask) + || archive.equals(getProject().getTasks().findByName( + RepackageTask.this.withJarTask.toString())))) { + getLogger().info( + "Jar task not repackaged (didn't match withJarTask): " + archive); return; } - if ("".equals(archive.getClassifier())) { + if ("".equals(archive.getClassifier()) + || RepackageTask.this.withJarTask != null) { File file = archive.getArchivePath(); if (file.exists()) { Repackager repackager = new LoggingRepackager(file); File out = RepackageTask.this.outputFile; - if (out != null) { + if (out != null && !file.equals(out)) { try { FileCopyUtils.copy(file, out); - } - catch (IOException ex) { + } catch (IOException ex) { throw new IllegalStateException(ex.getMessage(), ex); } file = out; @@ -161,8 +183,7 @@ public class RepackageTask extends DefaultTask { repackager.setBackupSource(this.extension.isBackupSource()); try { repackager.repackage(file, this.libraries); - } - catch (IOException ex) { + } catch (IOException ex) { throw new IllegalStateException(ex.getMessage(), ex); } } @@ -173,13 +194,11 @@ public class RepackageTask extends DefaultTask { String mainClass = (String) getProject().property("mainClassName"); if (RepackageTask.this.mainClass != null) { mainClass = RepackageTask.this.mainClass; - } - else if (this.extension.getMainClass() != null) { + } else if (this.extension.getMainClass() != null) { mainClass = this.extension.getMainClass(); - } - else if (getProject().getTasks().getByName("run").hasProperty("main")) { - mainClass = (String) getProject().getTasks().getByName("run") - .property("main"); + } else if (getProject().getTasks().getByName("run").hasProperty("main")) { + mainClass = (String) getProject().getTasks().getByName("run").property( + "main"); } getLogger().info("Setting mainClass: " + mainClass); repackager.setMainClass(mainClass); @@ -197,8 +216,7 @@ public class RepackageTask extends DefaultTask { long startTime = System.currentTimeMillis(); try { return super.findMainMethod(source); - } - finally { + } finally { long duration = System.currentTimeMillis() - startTime; if (duration > FIND_WARNING_TIMEOUT) { getLogger().warn(