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    /*
019     * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE
020     * file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file
021     * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
022     * License. You may obtain a copy of the License at
023     *
024     * http://www.apache.org/licenses/LICENSE-2.0
025     *
026     * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
027     * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
028     * specific language governing permissions and limitations under the License.
029     */
030    
031    import java.io.File;
032    import java.io.FileInputStream;
033    import java.io.IOException;
034    import java.io.InputStream;
035    import java.util.ArrayList;
036    import java.util.Enumeration;
037    import java.util.List;
038    import java.util.Properties;
039    import java.util.Set;
040    
041    import org.apache.commons.io.IOUtils;
042    import org.apache.maven.plugin.AbstractMojo;
043    import org.apache.maven.plugin.MojoExecutionException;
044    import org.apache.maven.project.MavenProject;
045    import org.codehaus.plexus.util.StringUtils;
046    import org.codehaus.plexus.util.cli.CommandLineUtils;
047    
048    /**
049     * The read-project-properties goal reads property files and stores the properties as project properties. It serves as
050     * an alternate to specifying properties in pom.xml.
051     *
052     * @author <a href="mailto:zarars@gmail.com">Zarar Siddiqi</a>
053     * @author <a href="mailto:Krystian.Nowak@gmail.com">Krystian Nowak</a>
054     * @version $Id: ReadPropertiesMojo.java 8861 2009-01-21 15:35:38Z pgier $
055     * @goal read-project-properties
056     */
057    public class ReadPropertiesMojo extends AbstractMojo {
058    
059        /**
060         * @parameter default-value="${project}"
061         * @required
062         * @readonly
063         */
064        private MavenProject project;
065    
066        /**
067         * The properties files that will be used when reading properties. Can use both .properties and .xml files
068         *
069         * @parameter
070         * @required
071         */
072        private File[] files;
073    
074        /**
075         * If true, the plugin will silently ignore any non-existent properties files, and the build will continue
076         *
077         * @parameter expression="${properties.quiet}" default-value="false"
078         */
079        private boolean quiet;
080    
081        /**
082         * Comma separated list of property values to ignore
083         *
084         * @parameter expression="${properties.ignore}"
085         */
086        private String ignore;
087    
088        @Override
089        public void execute() throws MojoExecutionException {
090            List<String> ignoreList = getListFromCSV(ignore);
091            Properties projectProperties = project.getProperties();
092            for (int i = 0; i < files.length; i++) {
093                File file = files[i];
094                if (validate(file)) {
095                    Properties p = getProperties(file);
096                    updateProperties(projectProperties, p, ignoreList);
097                }
098            }
099    
100            boolean useEnvVariables = false;
101            for (Enumeration<?> n = projectProperties.propertyNames(); n.hasMoreElements();) {
102                String k = (String) n.nextElement();
103                String p = (String) projectProperties.get(k);
104                if (p.indexOf("${env.") != -1) {
105                    useEnvVariables = true;
106                    break;
107                }
108            }
109            Properties environment = null;
110            if (useEnvVariables) {
111                try {
112                    environment = CommandLineUtils.getSystemEnvVars();
113                } catch (IOException e) {
114                    throw new MojoExecutionException("Error getting system envorinment variables: ", e);
115                }
116            }
117            for (Enumeration<?> n = projectProperties.propertyNames(); n.hasMoreElements();) {
118                String k = (String) n.nextElement();
119                projectProperties.setProperty(k, getPropertyValue(k, projectProperties, environment));
120            }
121        }
122    
123        protected void updateProperties(Properties p1, Properties p2, List<String> ignore) {
124            Set<String> names = p2.stringPropertyNames();
125            for (String name : names) {
126                if (!ignore.contains(name)) {
127                    String value = p2.getProperty(name);
128                    p1.setProperty(name, value);
129                }
130            }
131        }
132    
133        protected List<String> getListFromCSV(String csv) {
134            if (StringUtils.isBlank(csv)) {
135                return new ArrayList<String>();
136            }
137            List<String> list = new ArrayList<String>();
138            String[] tokens = StringUtils.split(csv, ",");
139            for (String token : tokens) {
140                list.add(token.trim());
141            }
142            return list;
143        }
144    
145        /**
146         * Retrieves a property value, replacing values like ${token} using the Properties to look them up. Shamelessly
147         * adapted from:
148         * http://maven.apache.org/plugins/maven-war-plugin/xref/org/apache/maven/plugin/war/PropertyUtils.html
149         *
150         * It will leave unresolved properties alone, trying for System properties, and environment variables and implements
151         * reparsing (in the case that the value of a property contains a key), and will not loop endlessly on a pair like
152         * test = ${test}
153         *
154         * @param k
155         *            property key
156         * @param p
157         *            project properties
158         * @param environment
159         *            environment variables
160         * @return resolved property value
161         */
162        protected String getPropertyValue(String k, Properties p, Properties environment) {
163            String v = p.getProperty(k);
164            String ret = "";
165            int idx, idx2;
166    
167            while ((idx = v.indexOf("${")) >= 0) {
168                // append prefix to result
169                ret += v.substring(0, idx);
170    
171                // strip prefix from original
172                v = v.substring(idx + 2);
173    
174                idx2 = v.indexOf("}");
175    
176                // if no matching } then bail
177                if (idx2 < 0) {
178                    break;
179                }
180    
181                // strip out the key and resolve it
182                // resolve the key/value for the ${statement}
183                String nk = v.substring(0, idx2);
184                v = v.substring(idx2 + 1);
185                String nv = p.getProperty(nk);
186    
187                // try global environment
188                if (nv == null) {
189                    nv = System.getProperty(nk);
190                }
191    
192                // try environment variable
193                if (nv == null && nk.startsWith("env.") && environment != null) {
194                    nv = environment.getProperty(nk.substring(4));
195                }
196    
197                // if the key cannot be resolved,
198                // leave it alone ( and don't parse again )
199                // else prefix the original string with the
200                // resolved property ( so it can be parsed further )
201                // taking recursion into account.
202                if (nv == null || nv.equals(nk)) {
203                    ret += "${" + nk + "}";
204                } else {
205                    v = nv + v;
206                }
207            }
208            return ret + v;
209        }
210    
211        protected boolean validate(File file) throws MojoExecutionException {
212            boolean exists = file != null && file.exists();
213            if (exists) {
214                return true;
215            }
216            if (quiet) {
217                getLog().info("Ignoring non-existent properties file '" + file + "'");
218                return false;
219            } else {
220                throw new MojoExecutionException("Non-existent properties file '" + file + "'");
221            }
222        }
223    
224        protected Properties getProperties(File file) throws MojoExecutionException {
225            InputStream in = null;
226            try {
227                getLog().info("Loading " + file);
228                Properties properties = new Properties();
229                in = new FileInputStream(file);
230                String filename = file.getName().toLowerCase();
231                if (filename.endsWith(".xml")) {
232                    properties.loadFromXML(in);
233                } else {
234                    properties.load(in);
235                }
236                return properties;
237            } catch (IOException e) {
238                throw new MojoExecutionException("Error reading properties file " + file.getAbsolutePath(), e);
239            } finally {
240                IOUtils.closeQuietly(in);
241            }
242        }
243    
244        public File[] getFiles() {
245            return files;
246        }
247    
248        public void setFiles(File[] files) {
249            this.files = files;
250        }
251    
252        public boolean isQuiet() {
253            return quiet;
254        }
255    
256        public void setQuiet(boolean quiet) {
257            this.quiet = quiet;
258        }
259    
260        public String getIgnore() {
261            return ignore;
262        }
263    
264        public void setIgnore(String ignoreProperties) {
265            this.ignore = ignoreProperties;
266        }
267    
268        public MavenProject getProject() {
269            return project;
270        }
271    
272    }