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 }