001/* 002 * Copyright 2012-2016 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.boot.loader.tools; 018 019import java.io.File; 020import java.io.FileOutputStream; 021import java.io.IOException; 022import java.text.SimpleDateFormat; 023import java.util.Date; 024import java.util.Map; 025import java.util.Map.Entry; 026import java.util.Properties; 027 028/** 029 * A {@code BuildPropertiesWriter} writes the {@code build-info.properties} for 030 * consumption by the Actuator. 031 * 032 * @author Andy Wilkinson 033 * @author Stephane Nicoll 034 */ 035public final class BuildPropertiesWriter { 036 037 private final File outputFile; 038 039 /** 040 * Creates a new {@code BuildPropertiesWriter} that will write to the given 041 * {@code outputFile}. 042 * @param outputFile the output file 043 */ 044 public BuildPropertiesWriter(File outputFile) { 045 this.outputFile = outputFile; 046 } 047 048 public void writeBuildProperties(ProjectDetails projectDetails) throws IOException { 049 Properties properties = createBuildInfo(projectDetails); 050 createFileIfNecessary(this.outputFile); 051 FileOutputStream outputStream = new FileOutputStream(this.outputFile); 052 try { 053 properties.store(outputStream, "Properties"); 054 } 055 finally { 056 try { 057 outputStream.close(); 058 } 059 catch (IOException ex) { 060 // Continue 061 } 062 } 063 } 064 065 private void createFileIfNecessary(File file) throws IOException { 066 if (file.exists()) { 067 return; 068 } 069 File parent = file.getParentFile(); 070 if (!parent.isDirectory() && !parent.mkdirs()) { 071 throw new IllegalStateException("Cannot create parent directory for '" 072 + this.outputFile.getAbsolutePath() + "'"); 073 } 074 if (!file.createNewFile()) { 075 throw new IllegalStateException("Cannot create target file '" 076 + this.outputFile.getAbsolutePath() + "'"); 077 } 078 } 079 080 protected Properties createBuildInfo(ProjectDetails project) { 081 Properties properties = new Properties(); 082 properties.put("build.group", project.getGroup()); 083 properties.put("build.artifact", project.getArtifact()); 084 properties.put("build.name", project.getName()); 085 properties.put("build.version", project.getVersion()); 086 properties.put("build.time", formatDate(new Date())); 087 if (project.getAdditionalProperties() != null) { 088 for (Map.Entry<String, String> entry : project.getAdditionalProperties() 089 .entrySet()) { 090 properties.put("build." + entry.getKey(), entry.getValue()); 091 } 092 } 093 return properties; 094 } 095 096 private String formatDate(Date date) { 097 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); 098 return sdf.format(date); 099 } 100 101 /** 102 * Build-system agnostic details of a project. 103 */ 104 public static final class ProjectDetails { 105 106 private final String group; 107 108 private final String artifact; 109 110 private final String name; 111 112 private final String version; 113 114 private final Map<String, String> additionalProperties; 115 116 public ProjectDetails(String group, String artifact, String version, String name, 117 Map<String, String> additionalProperties) { 118 this.group = group; 119 this.artifact = artifact; 120 this.name = name; 121 this.version = version; 122 validateAdditionalProperties(additionalProperties); 123 this.additionalProperties = additionalProperties; 124 } 125 126 private static void validateAdditionalProperties( 127 Map<String, String> additionalProperties) { 128 if (additionalProperties != null) { 129 for (Entry<String, String> property : additionalProperties.entrySet()) { 130 if (property.getValue() == null) { 131 throw new NullAdditionalPropertyValueException(property.getKey()); 132 } 133 } 134 } 135 } 136 137 public String getGroup() { 138 return this.group; 139 } 140 141 public String getArtifact() { 142 return this.artifact; 143 } 144 145 public String getName() { 146 return this.name; 147 } 148 149 public String getVersion() { 150 return this.version; 151 } 152 153 public Map<String, String> getAdditionalProperties() { 154 return this.additionalProperties; 155 } 156 157 } 158 159 /** 160 * Exception thrown when an additional property with a null value is encountered. 161 */ 162 public static class NullAdditionalPropertyValueException 163 extends IllegalArgumentException { 164 165 public NullAdditionalPropertyValueException(String name) { 166 super("Additional property '" + name + "' is illegal as its value is null"); 167 } 168 169 } 170 171}