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