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.kuali.maven.common.PropertiesUtils;
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.<br>
040     *
041     * Properties files handled by this plugin, have their property values resolved using Spring's expression parser. This
042     * means anything you can do with Spring property values you can do with property values handled by this plugin. For
043     * example, nested properties are supported: eg foo=${a.${b}.c}
044     *
045     * @author <a href="mailto:zarars@gmail.com">Zarar Siddiqi</a>
046     * @author <a href="mailto:Krystian.Nowak@gmail.com">Krystian Nowak</a>
047     * @auther Jeff Caddel
048     * @version $Id: ReadPropertiesMojo.java 8861 2009-01-21 15:35:38Z pgier $
049     * @goal read-project-properties
050     */
051    public class ReadPropertiesMojo extends AbstractMojo {
052        PropertiesUtils utils = new PropertiesUtils();
053    
054        /**
055         * @parameter default-value="${project}"
056         * @required
057         * @readonly
058         */
059        private MavenProject project;
060    
061        /**
062         * Locations where properties files can be found. Any url Spring resource loading can understand is valid. eg
063         * <code>classpath:myprops.properties</code>. Both, .properties and .xml style properties are supported.
064         *
065         * @parameter
066         * @required
067         */
068        private String[] locations;
069    
070        /**
071         * If true, the plugin will silently ignore any non-existent properties files, and the build will continue
072         *
073         * @parameter expression="${properties.quiet}" default-value="false"
074         */
075        private boolean quiet;
076    
077        /**
078         * If true, the plugin will operate silently without emitting any log messages
079         *
080         * @parameter expression="${properties.silent}" default-value="false"
081         */
082        private boolean silent;
083    
084        /**
085         * If true, the plugin will emit more verbose logging messages.
086         *
087         * @parameter expression="${properties.verbose}" default-value="false"
088         */
089        private boolean verbose;
090    
091        /**
092         * Comma separated list of property values to ignore
093         *
094         * @parameter expression="${properties.ignore}"
095         */
096        private String ignore;
097    
098        @Override
099        public void execute() throws MojoExecutionException {
100            // Figure out if there are properties we need to ignore
101            List<String> ignoreList = getIgnoreList();
102    
103            // Update project properties by loading in properties from the locations they've specified
104            updateProjectProperties(ignoreList);
105    
106            // Project + system + env properties
107            Properties allProperties = utils.getMavenProperties(project);
108            resolveValues(project.getProperties(), allProperties);
109    
110        }
111    
112        protected void resolveValues(Properties p1, Properties p2) {
113            for (String name : p1.stringPropertyNames()) {
114                String originalValue = p1.getProperty(name);
115                String resolvedValue = utils.getResolvedValue(originalValue, p2);
116                p1.setProperty(name, resolvedValue);
117            }
118        }
119    
120        /**
121         * Copy properties from p2 into p1, overwriting p1 values as we go.
122         *
123         * Ignore any properties with a key that appears in the ignore list
124         *
125         * @param p1
126         * @param p2
127         * @param ignore
128         */
129        protected void updateProperties(Properties p1, Properties p2, List<String> ignore) {
130            Set<String> names = p2.stringPropertyNames();
131            for (String name : names) {
132                if (!ignore.contains(name)) {
133                    String value = p2.getProperty(name);
134                    p1.setProperty(name, value);
135                }
136            }
137        }
138    
139        protected static final List<String> getListFromCSV(String csv) {
140            if (StringUtils.isBlank(csv)) {
141                return new ArrayList<String>();
142            }
143            List<String> list = new ArrayList<String>();
144            String[] tokens = StringUtils.split(csv, ",");
145            for (String token : tokens) {
146                list.add(token.trim());
147            }
148            return list;
149        }
150    
151        protected String toEmpty(String s) {
152            if (StringUtils.isBlank(s)) {
153                return "";
154            } else {
155                return s;
156            }
157        }
158    
159        protected boolean exists(String location) {
160            if (StringUtils.isBlank(location)) {
161                return false;
162            }
163            File file = new File(location);
164            if (file.exists()) {
165                return true;
166            }
167            ResourceLoader loader = new DefaultResourceLoader();
168            Resource resource = loader.getResource(location);
169            return resource.exists();
170        }
171    
172        protected boolean validate(String location) throws MojoExecutionException {
173            boolean exists = exists(location);
174            if (exists) {
175                return true;
176            }
177            if (quiet) {
178                if (verbose && !silent) {
179                    getLog().info("Ignoring non-existent properties file '" + toEmpty(location) + "'");
180                }
181                return false;
182            } else {
183                throw new MojoExecutionException("Non-existent properties file '" + location + "'");
184            }
185        }
186    
187        protected InputStream getInputStream(String location) throws IOException {
188            File file = new File(location);
189            if (file.exists()) {
190                return new FileInputStream(location);
191            }
192            ResourceLoader loader = new DefaultResourceLoader();
193            Resource resource = loader.getResource(location);
194            return resource.getInputStream();
195        }
196    
197        protected Properties getProperties(String location) throws MojoExecutionException {
198            InputStream in = null;
199            try {
200                Properties properties = new Properties();
201                in = getInputStream(location);
202                if (location.toLowerCase().endsWith(".xml")) {
203                    properties.loadFromXML(in);
204                } else {
205                    properties.load(in);
206                }
207                return properties;
208            } catch (IOException e) {
209                throw new MojoExecutionException("Error reading properties file " + location, e);
210            } finally {
211                IOUtils.closeQuietly(in);
212            }
213        }
214    
215        public boolean isQuiet() {
216            return quiet;
217        }
218    
219        public void setQuiet(boolean quiet) {
220            this.quiet = quiet;
221        }
222    
223        public String getIgnore() {
224            return ignore;
225        }
226    
227        public void setIgnore(String ignoreProperties) {
228            this.ignore = ignoreProperties;
229        }
230    
231        public MavenProject getProject() {
232            return project;
233        }
234    
235        public String[] getLocations() {
236            return locations;
237        }
238    
239        public void setLocations(String[] locations) {
240            this.locations = locations;
241        }
242    
243        public boolean isVerbose() {
244            return verbose;
245        }
246    
247        public void setVerbose(boolean verbose) {
248            this.verbose = verbose;
249        }
250    
251        public boolean isSilent() {
252            return silent;
253        }
254    
255        public void setSilent(boolean silent) {
256            this.silent = silent;
257        }
258    
259        protected List<String> getIgnoreList() {
260            List<String> ignoreList = getListFromCSV(ignore);
261            if (!silent && verbose && !StringUtils.isBlank(ignore)) {
262                getLog().info("Ignoring " + ignore);
263            }
264            return ignoreList;
265        }
266    
267        protected void updateProjectProperties(List<String> ignoreList) throws MojoExecutionException {
268            Properties projectProperties = project.getProperties();
269            for (int i = 0; i < locations.length; i++) {
270                Properties allProperties = utils.getMavenProperties(project);
271                String originalLocation = locations[i];
272                String resolvedLocation = utils.getResolvedValue(originalLocation, allProperties);
273                getLog().debug("o=" + originalLocation + " r=" + resolvedLocation);
274                if (!validate(resolvedLocation)) {
275                    continue;
276                }
277                if (!silent) {
278                    getLog().info("Loading " + resolvedLocation);
279                }
280    
281                // Load properties from this location
282                Properties p = getProperties(resolvedLocation);
283    
284                // Update project properties from the properties we just loaded
285                updateProperties(projectProperties, p, ignoreList);
286            }
287        }
288    
289    }