diff --git a/spring-boot-samples/spring-boot-sample-actuator/build.gradle b/spring-boot-samples/spring-boot-sample-actuator/build.gradle index 6498541b1c..6413d1c946 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/build.gradle +++ b/spring-boot-samples/spring-boot-sample-actuator/build.gradle @@ -22,9 +22,11 @@ apply plugin: 'spring-boot' jar { baseName = 'spring-boot-sample-actuator' - version = '0.0.0' } +group = 'org.springframework.boot' +version = springBootVersion + repositories { // NOTE: You should declare only repositories that you need here mavenLocal() @@ -59,3 +61,7 @@ springBoot { task wrapper(type: Wrapper) { gradleVersion = '1.6' } + +springBoot { + buildInfo() +} \ No newline at end of file diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/SpringBootPlugin.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/SpringBootPlugin.java index 945dfd218d..c1d5911ee6 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/SpringBootPlugin.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/SpringBootPlugin.java @@ -39,7 +39,8 @@ public class SpringBootPlugin implements Plugin { @Override public void apply(Project project) { - project.getExtensions().create("springBoot", SpringBootPluginExtension.class); + project.getExtensions().create("springBoot", SpringBootPluginExtension.class, + project); project.getPlugins().apply(JavaPlugin.class); new AgentPluginFeatures().apply(project); new RepackagePluginFeatures().apply(project); 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 1f4ff7ecdc..ddd648ebd0 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 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. @@ -20,6 +20,11 @@ import java.io.File; 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; @@ -39,9 +44,12 @@ import org.springframework.boot.loader.tools.Layouts; * @author Phillip Webb * @author Dave Syer * @author Stephane Nicoll + * @author Andy Wilkinson */ public class SpringBootPluginExtension { + private final Project project; + /** * 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 @@ -128,6 +136,10 @@ public class SpringBootPluginExtension { */ Map embeddedLaunchScriptProperties; + public SpringBootPluginExtension(Project project) { + this.project = project; + } + /** * Convenience method for use in a custom task. * @return the Layout to use or null if not explicitly set @@ -249,6 +261,21 @@ public class SpringBootPluginExtension { this.embeddedLaunchScriptProperties = embeddedLaunchScriptProperties; } + public void buildInfo() { + this.buildInfo(null); + } + + public void buildInfo(Closure taskConfigurer) { + BuildInfo buildInfo = this.project.getTasks().create("buildInfo", + BuildInfo.class); + this.project.getTasks().getByName(JavaPlugin.CLASSES_TASK_NAME) + .dependsOn(buildInfo); + if (taskConfigurer != null) { + taskConfigurer.setDelegate(buildInfo); + taskConfigurer.call(); + } + } + /** * Layout Types. */ diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/buildinfo/BuildInfo.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/buildinfo/BuildInfo.java new file mode 100644 index 0000000000..bfd510cf58 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/buildinfo/BuildInfo.java @@ -0,0 +1,137 @@ +/* + * Copyright 2012-2016 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.buildinfo; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import org.gradle.api.DefaultTask; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.TaskAction; +import org.gradle.api.tasks.TaskExecutionException; +import org.gradle.api.tasks.bundling.Jar; + +import org.springframework.boot.loader.tools.BuildPropertiesWriter; +import org.springframework.boot.loader.tools.BuildPropertiesWriter.ProjectDetails; + +/** + * {@link DefaultTask} for generating a {@code build.properties} file from a + * {@code Project}. + *

+ * By default, the {@code build.properties} file is generated in + * project.buildDir/resources/main/META-INF/boot. + *

+ * + * @author Andy Wilkinson + */ +public class BuildInfo extends DefaultTask { + + @OutputFile + private File outputFile = getProject().file(new File(getProject().getBuildDir(), + "resources/main/META-INF/boot/build.properties")); + + @Input + private String projectGroup = getProject().getGroup().toString(); + + @Input + private String projectArtifact = ((Jar) getProject().getTasks() + .getByName(JavaPlugin.JAR_TASK_NAME)).getBaseName(); + + @Input + private String projectVersion = getProject().getVersion().toString(); + + @Input + private String projectName = getProject().getName(); + + @Input + private Map additionalProperties = new HashMap(); + + @TaskAction + public void generateBuildProperties() { + try { + new BuildPropertiesWriter(this.outputFile) + .writeBuildProperties(new ProjectDetails(this.projectGroup, + this.projectArtifact, this.projectVersion, this.projectName, + coerceToStringValues(this.additionalProperties))); + } + catch (IOException ex) { + throw new TaskExecutionException(this, ex); + } + } + + public String getProjectGroup() { + return this.projectGroup; + } + + public void setProjectGroup(String projectGroup) { + this.projectGroup = projectGroup; + } + + public String getProjectArtifact() { + return this.projectArtifact; + } + + public void setProjectArtifact(String projectArtifact) { + this.projectArtifact = projectArtifact; + } + + public String getProjectVersion() { + return this.projectVersion; + } + + public void setProjectVersion(String projectVersion) { + this.projectVersion = projectVersion; + } + + public String getProjectName() { + return this.projectName; + } + + public void setProjectName(String projectName) { + this.projectName = projectName; + } + + public File getOutputFile() { + return this.outputFile; + } + + public void setOutputFile(File outputFile) { + this.outputFile = outputFile; + } + + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + public void setAdditionalProperties(Map additionalProperties) { + this.additionalProperties = additionalProperties; + } + + private Map coerceToStringValues(Map input) { + Map output = new HashMap(); + for (Entry entry : this.additionalProperties.entrySet()) { + output.put(entry.getKey(), entry.getValue().toString()); + } + return output; + } + +} diff --git a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/BuildPropertiesWriter.java b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/BuildPropertiesWriter.java new file mode 100644 index 0000000000..8a0ec51eaf --- /dev/null +++ b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/BuildPropertiesWriter.java @@ -0,0 +1,146 @@ +/* + * Copyright 2012-2016 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.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Map; +import java.util.Properties; + +/** + * A {@code BuildPropertiesWriter} writes the {@code build.properties} for consumption by + * the Actuator. + * + * @author Andy Wilkinson + * @author Stephane Nicoll + */ +public final class BuildPropertiesWriter { + + private final File outputFile; + + /** + * Creates a new {@code BuildPropertiesWriter} that will write to the given + * {@code outputFile}. + * + * @param outputFile the output file + */ + public BuildPropertiesWriter(File outputFile) { + this.outputFile = outputFile; + } + + public void writeBuildProperties(ProjectDetails projectDetails) throws IOException { + Properties properties = createBuildInfo(projectDetails); + createFileIfNecessary(this.outputFile); + FileOutputStream outputStream = new FileOutputStream(this.outputFile); + try { + properties.store(outputStream, "Properties"); + } + finally { + try { + outputStream.close(); + } + catch (IOException ex) { + // Continue + } + } + } + + private void createFileIfNecessary(File file) throws IOException { + if (file.exists()) { + return; + } + File parent = file.getParentFile(); + if (!parent.isDirectory() && !parent.mkdirs()) { + throw new IllegalStateException("Cannot create parent directory for '" + + this.outputFile.getAbsolutePath() + "'"); + } + if (!file.createNewFile()) { + throw new IllegalStateException("Cannot create target file '" + + this.outputFile.getAbsolutePath() + "'"); + } + } + + protected Properties createBuildInfo(ProjectDetails project) { + Properties properties = new Properties(); + properties.put("build.group", project.getGroup()); + properties.put("build.artifact", project.getArtifact()); + properties.put("build.name", project.getName()); + properties.put("build.version", project.getVersion()); + properties.put("build.time", formatDate(new Date())); + if (project.getAdditionalProperties() != null) { + for (Map.Entry entry : project.getAdditionalProperties() + .entrySet()) { + properties.put("build." + entry.getKey(), entry.getValue()); + } + } + return properties; + } + + private String formatDate(Date date) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); + return sdf.format(date); + } + + /** + * Build-system agnostic details of a project. + */ + public static final class ProjectDetails { + + private final String group; + + private final String artifact; + + private final String name; + + private final String version; + + private final Map additionalProperties; + + public ProjectDetails(String group, String artifact, String version, String name, + Map additionalProperties) { + this.group = group; + this.artifact = artifact; + this.name = name; + this.version = version; + this.additionalProperties = additionalProperties; + } + + public String getGroup() { + return this.group; + } + + public String getArtifact() { + return this.artifact; + } + + public String getName() { + return this.name; + } + + public String getVersion() { + return this.version; + } + + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + } +} diff --git a/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/BuildInfoMojo.java b/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/BuildInfoMojo.java index 0df02016c9..04f2ee2184 100644 --- a/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/BuildInfoMojo.java +++ b/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/BuildInfoMojo.java @@ -17,14 +17,7 @@ package org.springframework.boot.maven; import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.text.SimpleDateFormat; -import java.util.Date; import java.util.Map; -import java.util.Properties; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; @@ -34,6 +27,9 @@ import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.project.MavenProject; +import org.springframework.boot.loader.tools.BuildPropertiesWriter; +import org.springframework.boot.loader.tools.BuildPropertiesWriter.ProjectDetails; + /** * Generate a {@code build.properties} file based the content of the current * {@link MavenProject}. @@ -65,68 +61,15 @@ public class BuildInfoMojo extends AbstractMojo { @Override public void execute() throws MojoExecutionException, MojoFailureException { - Properties properties = createBuildInfo(); try { - createFileIfNecessary(this.outputFile); - FileOutputStream outputStream = new FileOutputStream(this.outputFile); - try { - properties.store(outputStream, "Properties"); - } - finally { - closeQuietly(outputStream); - } + new BuildPropertiesWriter(this.outputFile) + .writeBuildProperties(new ProjectDetails(this.project.getGroupId(), + this.project.getArtifactId(), this.project.getVersion(), + this.project.getName(), this.additionalProperties)); } - catch (FileNotFoundException ex) { + catch (Exception ex) { throw new MojoExecutionException(ex.getMessage(), ex); } - catch (IOException e) { - throw new MojoExecutionException(e.getMessage(), e); - } - } - - private void closeQuietly(OutputStream outputStream) { - try { - outputStream.close(); - } - catch (IOException ex) { - getLog().error("Error closing FileOutputStream: " + outputStream); - } - } - - protected Properties createBuildInfo() { - Properties properties = new Properties(); - properties.put("build.group", this.project.getGroupId()); - properties.put("build.artifact", this.project.getArtifactId()); - properties.put("build.name", this.project.getName()); - properties.put("build.version", this.project.getVersion()); - properties.put("build.time", formatDate(new Date())); - if (this.additionalProperties != null) { - for (Map.Entry entry : this.additionalProperties.entrySet()) { - properties.put("build." + entry.getKey(), entry.getValue()); - } - } - return properties; - } - - private String formatDate(Date date) { - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); - return sdf.format(date); - } - - private void createFileIfNecessary(File file) - throws MojoExecutionException, IOException { - if (file.exists()) { - return; - } - File parent = file.getParentFile(); - if (!parent.exists() && !parent.mkdirs()) { - throw new MojoExecutionException("Cannot create parent directory for '" - + this.outputFile.getAbsolutePath() + "'"); - } - if (!file.createNewFile()) { - throw new MojoExecutionException("Cannot create target file '" - + this.outputFile.getAbsolutePath() + "'"); - } } }