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 }