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
035 /**
036 * Write project properties to a file.
037 *
038 * @author Jeff Caddel
039 *
040 * @goal write-project-properties
041 */
042 public class WriteProjectProperties extends AbstractWritePropertiesMojo {
043 private static final String CR = "\r";
044 private static final String LF = "\n";
045 private static final String TAB = "\t";
046 private static final String[] ANT_ESCAPE_CHARS = { CR, LF, TAB, ":", "#", "=" };
047
048 /**
049 * Comma separated list of characters to escape when writing property values. cr=carriage return, lf=linefeed,
050 * tab=tab. Any other values are taken literally.
051 *
052 * @parameter default-value="cr,lf,tab" expression="${properties.escapeChars}"
053 */
054 private String escapeChars;
055
056 /**
057 * If true, the plugin will create the properties file formatted the same way Ant formats properties files using the
058 * <code>echoproperties</code> task. This mode adds 3 custom properties at the top of the file, DSTAMP, TODAY, and
059 * TSTAMP. In this mode <code>escapeChars</code> is ignored and the 6 characters Ant escapes are used instead
060 * <code>CR</code>,<code>LF</code>,<code>TAB</code>,<code>:</code>,<code>#</code>,<code>=</code>
061 *
062 * @parameter default-value="false" expression="${properties.antEchoPropertiesMode}"
063 */
064 private boolean antEchoPropertiesMode;
065
066 /**
067 * If true, the plugin will include system properties when writing the properties file. System properties override
068 * both environment variables and project properties.
069 *
070 * @parameter default-value="false" expression="${properties.includeSystemProperties}"
071 */
072 private boolean includeSystemProperties;
073
074 /**
075 * If true, the plugin will include environment variables when writing the properties file. Environment variables
076 * are prefixed with "env". Environment variables override project properties.
077 *
078 * @parameter default-value="false" expression="${properties.includeEnvironmentVariables}"
079 */
080 private boolean includeEnvironmentVariables;
081
082 /**
083 * Comma separated set of properties to exclude when writing the properties file
084 *
085 * @parameter expression="${properties.exclude}"
086 */
087 private String exclude;
088
089 /**
090 * Comma separated set of properties to write to the properties file. If provided, only the properties matching
091 * those supplied here will be written to the properties file.
092 *
093 * @parameter expression="${properties.include}"
094 */
095 private String include;
096
097 @Override
098 public void execute() throws MojoExecutionException, MojoFailureException {
099 Properties properties = new Properties();
100 // Add project properties
101 properties.putAll(project.getProperties());
102 if (includeEnvironmentVariables) {
103 // Add environment variables, overriding any existing properties with the same key
104 properties.putAll(getEnvironmentVariables());
105 }
106 if (includeSystemProperties) {
107 // Add system properties, overriding any existing properties with the same key
108 properties.putAll(System.getProperties());
109 }
110
111 // Remove properties as appropriate
112 trim(properties, exclude, include);
113
114 String comment = "# " + new Date() + "\n";
115 List<String> escapeTokens = getEscapeChars(escapeChars);
116 if (antEchoPropertiesMode) {
117 escapeTokens = Arrays.asList(ANT_ESCAPE_CHARS);
118 comment = getAntHeader();
119 properties.remove("DSTAMP");
120 properties.remove("TODAY");
121 properties.remove("TSTAMP");
122 }
123
124 getLog().info("Creating " + outputFile);
125 writeProperties(outputFile, comment, properties, escapeTokens);
126 }
127
128 protected Properties getEnvironmentVariables() {
129 String prefix = "env";
130 Map<String, String> map = System.getenv();
131 Properties props = new Properties();
132 for (String key : map.keySet()) {
133 String newKey = prefix + "." + key;
134 String value = map.get(key);
135 props.setProperty(newKey, value);
136 }
137 return props;
138 }
139
140 protected void trim(Properties properties, String omitCSV, String includeCSV) {
141 List<String> omitKeys = ReadPropertiesMojo.getListFromCSV(omitCSV);
142 for (String key : omitKeys) {
143 properties.remove(key);
144 }
145 if (StringUtils.isBlank(includeCSV)) {
146 return;
147 }
148 List<String> includeKeys = ReadPropertiesMojo.getListFromCSV(includeCSV);
149 Set<String> keys = properties.stringPropertyNames();
150 for (String key : keys) {
151 if (!includeKeys.contains(key)) {
152 properties.remove(key);
153 }
154 }
155 }
156
157 protected String getAntHeader() throws MojoExecutionException {
158 SimpleDateFormat dstamp = new SimpleDateFormat("yyyyMMdd");
159 SimpleDateFormat today = new SimpleDateFormat("MMMM d yyyy");
160 SimpleDateFormat tstamp = new SimpleDateFormat("HHmm");
161 Date now = new Date();
162 StringBuilder sb = new StringBuilder();
163 sb.append("# Ant properties\n");
164 sb.append("# " + now + "\n");
165 sb.append("DSTAMP=" + dstamp.format(now) + "\n");
166 sb.append("TODAY=" + today.format(now) + "\n");
167 sb.append("TSTAMP=" + tstamp.format(now) + "\n");
168 return sb.toString();
169 }
170
171 protected List<String> getEscapeChars(String escapeChars) {
172 List<String> tokens = ReadPropertiesMojo.getListFromCSV(escapeChars);
173 List<String> realTokens = new ArrayList<String>();
174 for (String token : tokens) {
175 String realToken = getRealToken(token);
176 realTokens.add(realToken);
177 }
178 return realTokens;
179 }
180
181 protected String getRealToken(String token) {
182 if (token.equalsIgnoreCase("CR")) {
183 return CR;
184 } else if (token.equalsIgnoreCase("LF")) {
185 return LF;
186 } else if (token.equalsIgnoreCase("TAB")) {
187 return TAB;
188 } else {
189 return token;
190 }
191 }
192
193 protected void writeProperties(File file, String comment, Properties properties, List<String> escapeTokens)
194 throws MojoExecutionException {
195 List<String> names = new ArrayList<String>(properties.stringPropertyNames());
196 Collections.sort(names);
197 StringBuilder sb = new StringBuilder();
198 if (!StringUtils.isBlank(comment)) {
199 sb.append(comment);
200 }
201 for (String name : names) {
202 String value = properties.getProperty(name);
203 String escapedValue = escape(value, escapeTokens);
204 sb.append(name + "=" + escapedValue + "\n");
205 }
206 try {
207 FileUtils.writeStringToFile(file, sb.toString());
208 } catch (IOException e) {
209 throw new MojoExecutionException("Error creating properties file", e);
210 }
211 }
212
213 protected String escape(String s, List<String> escapeChars) {
214 for (String escapeChar : escapeChars) {
215 s = s.replace(escapeChar, "\\" + escapeChar);
216 }
217 return s;
218
219 }
220
221 public boolean isAntEchoPropertiesMode() {
222 return antEchoPropertiesMode;
223 }
224
225 public void setAntEchoPropertiesMode(boolean antEchoPropertiesMode) {
226 this.antEchoPropertiesMode = antEchoPropertiesMode;
227 }
228
229 public boolean isIncludeSystemProperties() {
230 return includeSystemProperties;
231 }
232
233 public void setIncludeSystemProperties(boolean includeSystemProperties) {
234 this.includeSystemProperties = includeSystemProperties;
235 }
236 }