From 2433f721bca2082c8cba0dc29870dffc9a659f30 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Wed, 18 Jun 2014 16:02:26 +0100 Subject: [PATCH] Add 'classifier' property to Gradle plugin The default behaviour doesn't change with this commit, but now the user has the option to specify a 'classifier' property either in springBoot { classifier = 'exec' } (i.e. globally for all repackage tasks) or in each repackage task, e.g. bootRepackage { classifier = 'exec' }. In that case the original archive is not overwritten but copied into -.jar (or .war etc.) and then enhanced. Fixes gh-1113, fixes gh-141 also I believe. --- .../boot/gradle/ClassifierTests.java | 63 +++++++++++++++++++ .../resources/classifier-extension.gradle | 30 +++++++++ .../src/test/resources/classifier.gradle | 30 +++++++++ .../gradle/SpringBootPluginExtension.groovy | 12 +++- .../repackage/RepackagePluginFeatures.java | 63 +++++++++++++++++++ .../boot/gradle/repackage/RepackageTask.java | 42 +++++++++++-- 6 files changed, 233 insertions(+), 7 deletions(-) create mode 100644 spring-boot-integration-tests/src/test/java/org/springframework/boot/gradle/ClassifierTests.java create mode 100644 spring-boot-integration-tests/src/test/resources/classifier-extension.gradle create mode 100644 spring-boot-integration-tests/src/test/resources/classifier.gradle diff --git a/spring-boot-integration-tests/src/test/java/org/springframework/boot/gradle/ClassifierTests.java b/spring-boot-integration-tests/src/test/java/org/springframework/boot/gradle/ClassifierTests.java new file mode 100644 index 0000000000..390ec7e3ea --- /dev/null +++ b/spring-boot-integration-tests/src/test/java/org/springframework/boot/gradle/ClassifierTests.java @@ -0,0 +1,63 @@ +/* + * Copyright 2012-2014 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.gradle; + +import static org.junit.Assert.assertNotNull; + +import java.util.jar.JarFile; + +import org.gradle.tooling.ProjectConnection; +import org.junit.Test; +import org.springframework.boot.dependency.tools.ManagedDependencies; + +/** + * Tests for using the Gradle plugin's support for installing artifacts + * + * @author Dave Syer + */ +public class ClassifierTests { + + private ProjectConnection project; + + private static final String BOOT_VERSION = ManagedDependencies.get().find( + "spring-boot").getVersion(); + + @Test + public void classifierInBootTask() throws Exception { + project = new ProjectCreator().createProject("classifier"); + project.newBuild().forTasks("build").withArguments( + "-PbootVersion=" + BOOT_VERSION, "--stacktrace").run(); + checkFilesExist("classifier"); + } + + @Test + public void classifierInBootExtension() throws Exception { + project = new ProjectCreator().createProject("classifier-extension"); + project.newBuild().forTasks("build").withArguments( + "-PbootVersion=" + BOOT_VERSION, "--stacktrace", "--info").run(); + } + + private void checkFilesExist(String name) throws Exception { + JarFile jar = new JarFile("target/" + name + "/build/libs/" + name + ".jar"); + assertNotNull(jar.getManifest()); + jar.close(); + jar = new JarFile("target/" + name + "/build/libs/" + name + "-exec.jar"); + assertNotNull(jar.getManifest()); + jar.close(); + } + +} diff --git a/spring-boot-integration-tests/src/test/resources/classifier-extension.gradle b/spring-boot-integration-tests/src/test/resources/classifier-extension.gradle new file mode 100644 index 0000000000..da980ba30f --- /dev/null +++ b/spring-boot-integration-tests/src/test/resources/classifier-extension.gradle @@ -0,0 +1,30 @@ +buildscript { + repositories { + mavenLocal() + } + dependencies { + classpath("org.springframework.boot:spring-boot-gradle-plugin:${project.bootVersion}") + } +} + +apply plugin: 'java' +apply plugin: 'maven' +apply plugin: 'spring-boot' + +jar { + baseName = 'classifier-extension' +} + +springBoot { + classifier = 'exec' + mainClass = 'demo.Application' +} + +repositories { + mavenLocal() + mavenCentral() +} + +dependencies { + compile "org.springframework:spring-core" +} diff --git a/spring-boot-integration-tests/src/test/resources/classifier.gradle b/spring-boot-integration-tests/src/test/resources/classifier.gradle new file mode 100644 index 0000000000..e77c5dab3a --- /dev/null +++ b/spring-boot-integration-tests/src/test/resources/classifier.gradle @@ -0,0 +1,30 @@ +buildscript { + repositories { + mavenLocal() + } + dependencies { + classpath("org.springframework.boot:spring-boot-gradle-plugin:${project.bootVersion}") + } +} + +apply plugin: 'java' +apply plugin: 'maven' +apply plugin: 'spring-boot' + +jar { + baseName = 'classifier' +} + +bootRepackage { + classifier = 'exec' + mainClass = 'demo.Application' +} + +repositories { + mavenLocal() + mavenCentral() +} + +dependencies { + compile "org.springframework:spring-core" +} 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 0607308fb7..0563222183 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 @@ -58,12 +58,20 @@ public class SpringBootPluginExtension { } /** - * 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 + * The main class that should be run. Instead of setting this explicitly you can use the + * 'mainClassName' of the project or the 'main' of the 'run' task. If not specified the + * value from the MANIFEST will be used, or if no manifest entry is the archive will be * searched for a suitable class. */ String mainClass + /** + * The classifier (file name part before the extension). Instead of setting this explicitly + * you can use the 'classifier' property of the 'bootRepackage' task. If not specified the archive + * will be replaced instead of renamed. + */ + String classifier + /** * The name of the ivy configuration name to treat as 'provided' (when packaging * those dependencies in a separate path). If not specified 'providedRuntime' will 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 19bc3a5b68..0a9c33a589 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 @@ -16,11 +16,17 @@ package org.springframework.boot.gradle.repackage; +import java.io.File; + +import org.gradle.api.Action; import org.gradle.api.Project; import org.gradle.api.Task; import org.gradle.api.artifacts.Dependency; import org.gradle.api.plugins.BasePlugin; +import org.gradle.api.tasks.bundling.Jar; import org.springframework.boot.gradle.PluginFeatures; +import org.springframework.boot.gradle.SpringBootPluginExtension; +import org.springframework.util.StringUtils; /** * {@link PluginFeatures} to add repackage support. @@ -47,9 +53,19 @@ public class RepackagePluginFeatures implements PluginFeatures { 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)); + } + }); + } + private void ensureTaskRunsOnAssembly(Project project, Task task) { project.getTasks().getByName(BasePlugin.ASSEMBLE_TASK_NAME).dependsOn(task); } @@ -62,4 +78,51 @@ public class RepackagePluginFeatures implements PluginFeatures { .set("BootRepackage", RepackageTask.class); } + private class OutputAction implements Action { + + private RepackageTask task; + + public OutputAction(RepackageTask task) { + this.task = task; + } + + @Override + public void execute(Jar archive) { + if ("".equals(archive.getClassifier())) { + setClassifier(task, archive); + File file = archive.getArchivePath(); + String classifier = task.getClassifier(); + if (classifier != null) { + String withClassifer = file.getName(); + withClassifer = StringUtils.stripFilenameExtension(withClassifer) + + "-" + classifier + "." + + StringUtils.getFilenameExtension(withClassifer); + File out = new File(file.getParentFile(), withClassifer); + file = out; + task.getOutputs().file(file); + task.setOutputFile(file); + } + } + + } + + private void setClassifier(RepackageTask task, Jar archive) { + Project project = task.getProject(); + String classifier = null; + SpringBootPluginExtension extension = project.getExtensions().getByType( + SpringBootPluginExtension.class); + if (task.getClassifier() != null) { + classifier = task.getClassifier(); + } + else if (extension.getClassifier() != null) { + classifier = extension.getClassifier(); + } + if (classifier != null) { + project.getLogger().info("Setting classifier: " + classifier); + task.setClassifier(classifier); + } + } + + } + } 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 40a3413790..47b21fa634 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 @@ -27,6 +27,7 @@ import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.bundling.Jar; import org.springframework.boot.gradle.SpringBootPluginExtension; import org.springframework.boot.loader.tools.Repackager; +import org.springframework.util.FileCopyUtils; /** * Repackage task. @@ -44,6 +45,10 @@ public class RepackageTask extends DefaultTask { private String mainClass; + private String classifier; + + private File outputFile; + public void setCustomConfiguration(String customConfiguration) { this.customConfiguration = customConfiguration; } @@ -55,11 +60,19 @@ public class RepackageTask extends DefaultTask { public void setMainClass(String mainClass) { this.mainClass = mainClass; } - + public String getMainClass() { return mainClass; } + public String getClassifier() { + return classifier; + } + + public void setClassifier(String classifier) { + this.classifier = classifier; + } + @TaskAction public void repackage() { Project project = getProject(); @@ -102,13 +115,24 @@ public class RepackageTask extends DefaultTask { File file = archive.getArchivePath(); if (file.exists()) { Repackager repackager = new LoggingRepackager(file); + File out = RepackageTask.this.outputFile; + if (out != null) { + try { + FileCopyUtils.copy(file, out); + } + catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + file = out; + } + RepackageTask.this.outputFile = file; setMainClass(repackager); if (this.extension.convertLayout() != null) { repackager.setLayout(this.extension.convertLayout()); } repackager.setBackupSource(this.extension.isBackupSource()); try { - repackager.repackage(this.libraries); + repackager.repackage(file, this.libraries); } catch (IOException ex) { throw new IllegalStateException(ex.getMessage(), ex); @@ -121,10 +145,13 @@ 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); @@ -154,4 +181,9 @@ public class RepackageTask extends DefaultTask { } }; } + + void setOutputFile(File file) { + this.outputFile = file; + } + }