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