001 /** 002 * Copyright 2009-2012 The Kuali Foundation 003 * 004 * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php 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 package org.codehaus.mojo.properties; 017 018 import java.io.File; 019 import java.io.IOException; 020 import java.text.SimpleDateFormat; 021 import java.util.ArrayList; 022 import java.util.Arrays; 023 import java.util.Collections; 024 import java.util.Date; 025 import java.util.List; 026 import java.util.Map; 027 import java.util.Properties; 028 import java.util.Set; 029 030 import org.apache.commons.io.FileUtils; 031 import org.apache.maven.plugin.MojoExecutionException; 032 import org.apache.maven.plugin.MojoFailureException; 033 import org.codehaus.plexus.util.StringUtils; 034 import org.springframework.util.PropertyPlaceholderHelper; 035 036 /** 037 * Write project properties to a file. 038 * 039 * @author Jeff Caddel 040 * 041 * @goal write-project-properties 042 */ 043 public class WriteProjectProperties extends AbstractWritePropertiesMojo { 044 private static final String CR = "\r"; 045 private static final String LF = "\n"; 046 private static final String TAB = "\t"; 047 private static final String BACKSLASH = "\\"; 048 private static final String[] ANT_ESCAPE_CHARS = { CR, LF, TAB, BACKSLASH, ":", "#", "=" }; 049 050 /** 051 * Comma separated list of characters to escape when writing property values. cr=carriage return, lf=linefeed, tab=tab. Any other values 052 * are taken literally. 053 * 054 * @parameter default-value="cr,lf,tab,backslash,:,#,=" expression="${properties.escapeChars}" 055 */ 056 private String escapeChars; 057 058 /** 059 * If true, the plugin will create the properties file formatted the same way Ant formats properties files using the 060 * <code>echoproperties</code> task. This mode adds 3 custom properties at the top of the file, DSTAMP, TODAY, and TSTAMP. In this mode 061 * <code>escapeChars</code> is ignored and the 6 characters Ant escapes are used instead <code>CR</code>,<code>LF</code>, 062 * <code>TAB</code>,<code>:</code>,<code>#</code>,<code>=</code> 063 * 064 * @parameter default-value="false" expression="${properties.antEchoPropertiesMode}" 065 */ 066 private boolean antEchoPropertiesMode; 067 068 /** 069 * If true, the plugin will include system properties when writing the properties file. System properties override both environment 070 * variables and project properties. 071 * 072 * @parameter default-value="false" expression="${properties.includeSystemProperties}" 073 */ 074 private boolean includeSystemProperties; 075 076 /** 077 * If true, the plugin will include environment variables when writing the properties file. Environment variables are prefixed with 078 * "env". Environment variables override project properties. 079 * 080 * @parameter default-value="false" expression="${properties.includeEnvironmentVariables}" 081 */ 082 private boolean includeEnvironmentVariables; 083 084 /** 085 * Comma separated set of properties to exclude when writing the properties file 086 * 087 * @parameter expression="${properties.exclude}" 088 */ 089 private String exclude; 090 091 /** 092 * Comma separated set of properties to write to the properties file. If provided, only the properties matching those supplied here will 093 * be written to the properties file. 094 * 095 * @parameter expression="${properties.include}" 096 */ 097 private String include; 098 099 /** 100 * If true placeholders are resolved before writing properties to the file 101 * 102 * @parameter expression="${properties.resolvePlaceholders}" 103 */ 104 private boolean resolvePlaceholders; 105 106 @Override 107 public void execute() throws MojoExecutionException, MojoFailureException { 108 Properties properties = new Properties(); 109 // Add project properties 110 properties.putAll(project.getProperties()); 111 if (includeEnvironmentVariables) { 112 // Add environment variables, overriding any existing properties with the same key 113 properties.putAll(getEnvironmentVariables()); 114 } 115 if (includeSystemProperties) { 116 // Add system properties, overriding any existing properties with the same key 117 properties.putAll(System.getProperties()); 118 } 119 120 // Remove properties as appropriate 121 trim(properties, exclude, include); 122 123 if (resolvePlaceholders) { 124 properties = getResolvedProperties(properties); 125 } 126 127 String comment = "# " + new Date() + "\n"; 128 List<String> escapeTokens = getEscapeChars(escapeChars); 129 if (antEchoPropertiesMode) { 130 escapeTokens = Arrays.asList(ANT_ESCAPE_CHARS); 131 comment = getAntHeader(); 132 properties.remove("DSTAMP"); 133 properties.remove("TODAY"); 134 properties.remove("TSTAMP"); 135 } 136 137 getLog().info("Creating " + outputFile); 138 writeProperties(outputFile, comment, properties, escapeTokens); 139 } 140 141 protected Properties getResolvedProperties(Properties props) { 142 PropertyPlaceholderHelper pph = new PropertyPlaceholderHelper("${", "}"); 143 List<String> keys = new ArrayList<String>(props.stringPropertyNames()); 144 Collections.sort(keys); 145 Properties newProps = new Properties(); 146 for (String key : keys) { 147 String originalValue = props.getProperty(key); 148 String resolvedValue = pph.replacePlaceholders(originalValue, props); 149 newProps.setProperty(key, resolvedValue); 150 } 151 return newProps; 152 153 } 154 155 protected static Properties getEnvironmentVariables() { 156 String prefix = "env"; 157 Map<String, String> map = System.getenv(); 158 Properties props = new Properties(); 159 for (String key : map.keySet()) { 160 String newKey = prefix + "." + key; 161 String value = map.get(key); 162 props.setProperty(newKey, value); 163 } 164 return props; 165 } 166 167 protected void trim(Properties properties, String excludeCSV, String includeCSV) { 168 List<String> omitKeys = ReadPropertiesMojo.getListFromCSV(excludeCSV); 169 for (String key : omitKeys) { 170 properties.remove(key); 171 } 172 if (StringUtils.isBlank(includeCSV)) { 173 return; 174 } 175 List<String> includeKeys = ReadPropertiesMojo.getListFromCSV(includeCSV); 176 Set<String> keys = properties.stringPropertyNames(); 177 for (String key : keys) { 178 if (!includeKeys.contains(key)) { 179 properties.remove(key); 180 } 181 } 182 } 183 184 protected String getAntHeader() { 185 SimpleDateFormat dstamp = new SimpleDateFormat("yyyyMMdd"); 186 SimpleDateFormat today = new SimpleDateFormat("MMMM d yyyy"); 187 SimpleDateFormat tstamp = new SimpleDateFormat("HHmm"); 188 Date now = new Date(); 189 StringBuilder sb = new StringBuilder(); 190 sb.append("# Ant properties\n"); 191 sb.append("# " + now + "\n"); 192 sb.append("DSTAMP=" + dstamp.format(now) + "\n"); 193 sb.append("TODAY=" + today.format(now) + "\n"); 194 sb.append("TSTAMP=" + tstamp.format(now) + "\n"); 195 return sb.toString(); 196 } 197 198 protected List<String> getEscapeChars(String escapeChars) { 199 List<String> tokens = ReadPropertiesMojo.getListFromCSV(escapeChars); 200 List<String> realTokens = new ArrayList<String>(); 201 for (String token : tokens) { 202 String realToken = getRealToken(token); 203 realTokens.add(realToken); 204 } 205 return realTokens; 206 } 207 208 protected String getRealToken(String token) { 209 if (token.equalsIgnoreCase("CR")) { 210 return CR; 211 } else if (token.equalsIgnoreCase("LF")) { 212 return LF; 213 } else if (token.equalsIgnoreCase("TAB")) { 214 return TAB; 215 } else if (token.equalsIgnoreCase("BACKSLASH")) { 216 return BACKSLASH; 217 } else { 218 return token; 219 } 220 } 221 222 protected String getContent(String comment, Properties properties, List<String> escapeTokens) { 223 List<String> names = new ArrayList<String>(properties.stringPropertyNames()); 224 Collections.sort(names); 225 StringBuilder sb = new StringBuilder(); 226 if (!StringUtils.isBlank(comment)) { 227 sb.append(comment); 228 } 229 for (String name : names) { 230 String value = properties.getProperty(name); 231 String escapedValue = escape(value, escapeTokens); 232 sb.append(name + "=" + escapedValue + "\n"); 233 } 234 return sb.toString(); 235 } 236 237 protected void writeProperties(File file, String comment, Properties properties, List<String> escapeTokens) throws MojoExecutionException { 238 try { 239 String content = getContent(comment, properties, escapeTokens); 240 FileUtils.writeStringToFile(file, content); 241 } catch (IOException e) { 242 throw new MojoExecutionException("Error creating properties file", e); 243 } 244 } 245 246 protected String escape(String s, List<String> escapeChars) { 247 for (String escapeChar : escapeChars) { 248 String replacementToken = getReplacementToken(escapeChar); 249 s = s.replace(escapeChar, replacementToken); 250 } 251 return s; 252 } 253 254 protected String getReplacementToken(String escapeChar) { 255 if (escapeChar.equals(CR)) { 256 return "\\r"; 257 } else if (escapeChar.equals(LF)) { 258 return "\\n"; 259 } else if (escapeChar.equals(TAB)) { 260 return "\\t"; 261 } else 262 return "\\" + escapeChar; 263 } 264 265 public boolean isAntEchoPropertiesMode() { 266 return antEchoPropertiesMode; 267 } 268 269 public void setAntEchoPropertiesMode(boolean antEchoPropertiesMode) { 270 this.antEchoPropertiesMode = antEchoPropertiesMode; 271 } 272 273 public boolean isIncludeSystemProperties() { 274 return includeSystemProperties; 275 } 276 277 public void setIncludeSystemProperties(boolean includeSystemProperties) { 278 this.includeSystemProperties = includeSystemProperties; 279 } 280 281 public String getEscapeChars() { 282 return escapeChars; 283 } 284 285 public void setEscapeChars(String escapeChars) { 286 this.escapeChars = escapeChars; 287 } 288 289 public boolean isIncludeEnvironmentVariables() { 290 return includeEnvironmentVariables; 291 } 292 293 public void setIncludeEnvironmentVariables(boolean includeEnvironmentVariables) { 294 this.includeEnvironmentVariables = includeEnvironmentVariables; 295 } 296 297 public String getExclude() { 298 return exclude; 299 } 300 301 public void setExclude(String exclude) { 302 this.exclude = exclude; 303 } 304 305 public String getInclude() { 306 return include; 307 } 308 309 public void setInclude(String include) { 310 this.include = include; 311 } 312 313 public boolean isResolvePlaceholders() { 314 return resolvePlaceholders; 315 } 316 317 public void setResolvePlaceholders(boolean resolvePlaceholders) { 318 this.resolvePlaceholders = resolvePlaceholders; 319 } 320 }