View Javadoc

1   /**
2    * Copyright 2009-2012 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.codehaus.mojo.properties;
17  
18  import java.io.File;
19  import java.io.IOException;
20  import java.text.SimpleDateFormat;
21  import java.util.ArrayList;
22  import java.util.Arrays;
23  import java.util.Collections;
24  import java.util.Date;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Properties;
28  import java.util.Set;
29  
30  import org.apache.commons.io.FileUtils;
31  import org.apache.maven.plugin.MojoExecutionException;
32  import org.apache.maven.plugin.MojoFailureException;
33  import org.codehaus.plexus.util.StringUtils;
34  import org.springframework.util.PropertyPlaceholderHelper;
35  
36  /**
37   * Write project properties to a file.
38   * 
39   * @author Jeff Caddel
40   * 
41   * @goal write-project-properties
42   */
43  public class WriteProjectProperties extends AbstractWritePropertiesMojo {
44  	private static final String CR = "\r";
45  	private static final String LF = "\n";
46  	private static final String TAB = "\t";
47  	private static final String BACKSLASH = "\\";
48  	private static final String[] ANT_ESCAPE_CHARS = { CR, LF, TAB, BACKSLASH, ":", "#", "=" };
49  
50  	/**
51  	 * Comma separated list of characters to escape when writing property values. cr=carriage return, lf=linefeed, tab=tab. Any other values
52  	 * are taken literally.
53  	 * 
54  	 * @parameter default-value="cr,lf,tab,backslash,:,#,=" expression="${properties.escapeChars}"
55  	 */
56  	private String escapeChars;
57  
58  	/**
59  	 * If true, the plugin will create the properties file formatted the same way Ant formats properties files using the
60  	 * <code>echoproperties</code> task. This mode adds 3 custom properties at the top of the file, DSTAMP, TODAY, and TSTAMP. In this mode
61  	 * <code>escapeChars</code> is ignored and the 6 characters Ant escapes are used instead <code>CR</code>,<code>LF</code>,
62  	 * <code>TAB</code>,<code>:</code>,<code>#</code>,<code>=</code>
63  	 * 
64  	 * @parameter default-value="false" expression="${properties.antEchoPropertiesMode}"
65  	 */
66  	private boolean antEchoPropertiesMode;
67  
68  	/**
69  	 * If true, the plugin will include system properties when writing the properties file. System properties override both environment
70  	 * variables and project properties.
71  	 * 
72  	 * @parameter default-value="false" expression="${properties.includeSystemProperties}"
73  	 */
74  	private boolean includeSystemProperties;
75  
76  	/**
77  	 * If true, the plugin will include environment variables when writing the properties file. Environment variables are prefixed with
78  	 * "env". Environment variables override project properties.
79  	 * 
80  	 * @parameter default-value="false" expression="${properties.includeEnvironmentVariables}"
81  	 */
82  	private boolean includeEnvironmentVariables;
83  
84  	/**
85  	 * Comma separated set of properties to exclude when writing the properties file
86  	 * 
87  	 * @parameter expression="${properties.exclude}"
88  	 */
89  	private String exclude;
90  
91  	/**
92  	 * Comma separated set of properties to write to the properties file. If provided, only the properties matching those supplied here will
93  	 * be written to the properties file.
94  	 * 
95  	 * @parameter expression="${properties.include}"
96  	 */
97  	private String include;
98  
99  	/**
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 }