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