001    /**
002     * Copyright 2009-2013 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.List;
023    import java.util.Properties;
024    import java.util.Set;
025    
026    import org.apache.commons.io.IOUtils;
027    import org.apache.commons.lang.StringUtils;
028    import org.apache.maven.plugin.AbstractMojo;
029    import org.apache.maven.plugin.MojoExecutionException;
030    import org.apache.maven.project.MavenProject;
031    import org.kuali.common.util.CollectionUtils;
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 an alternate to
039     * 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 means anything you
042     * can do with Spring property values you can do with property values handled by this plugin. For example, nested properties are supported:
043     * 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 String toEmpty(String s) {
140                    if (StringUtils.isBlank(s)) {
141                            return "";
142                    } else {
143                            return s;
144                    }
145            }
146    
147            protected boolean exists(String location) {
148                    if (StringUtils.isBlank(location)) {
149                            return false;
150                    }
151                    File file = new File(location);
152                    if (file.exists()) {
153                            return true;
154                    }
155                    ResourceLoader loader = new DefaultResourceLoader();
156                    Resource resource = loader.getResource(location);
157                    return resource.exists();
158            }
159    
160            protected boolean validate(String location) throws MojoExecutionException {
161                    boolean exists = exists(location);
162                    if (exists) {
163                            return true;
164                    }
165                    if (quiet) {
166                            if (verbose && !silent) {
167                                    getLog().info("Ignoring non-existent properties file '" + toEmpty(location) + "'");
168                            }
169                            return false;
170                    } else {
171                            throw new MojoExecutionException("Non-existent properties file '" + location + "'");
172                    }
173            }
174    
175            protected InputStream getInputStream(String location) throws IOException {
176                    File file = new File(location);
177                    if (file.exists()) {
178                            return new FileInputStream(location);
179                    }
180                    ResourceLoader loader = new DefaultResourceLoader();
181                    Resource resource = loader.getResource(location);
182                    return resource.getInputStream();
183            }
184    
185            protected Properties getProperties(String location) throws MojoExecutionException {
186                    InputStream in = null;
187                    try {
188                            Properties properties = new Properties();
189                            in = getInputStream(location);
190                            if (location.toLowerCase().endsWith(".xml")) {
191                                    properties.loadFromXML(in);
192                            } else {
193                                    properties.load(in);
194                            }
195                            return properties;
196                    } catch (IOException e) {
197                            throw new MojoExecutionException("Error reading properties file " + location, e);
198                    } finally {
199                            IOUtils.closeQuietly(in);
200                    }
201            }
202    
203            public boolean isQuiet() {
204                    return quiet;
205            }
206    
207            public void setQuiet(boolean quiet) {
208                    this.quiet = quiet;
209            }
210    
211            public String getIgnore() {
212                    return ignore;
213            }
214    
215            public void setIgnore(String ignoreProperties) {
216                    this.ignore = ignoreProperties;
217            }
218    
219            public MavenProject getProject() {
220                    return project;
221            }
222    
223            public String[] getLocations() {
224                    return locations;
225            }
226    
227            public void setLocations(String[] locations) {
228                    this.locations = locations;
229            }
230    
231            public boolean isVerbose() {
232                    return verbose;
233            }
234    
235            public void setVerbose(boolean verbose) {
236                    this.verbose = verbose;
237            }
238    
239            public boolean isSilent() {
240                    return silent;
241            }
242    
243            public void setSilent(boolean silent) {
244                    this.silent = silent;
245            }
246    
247            protected List<String> getIgnoreList() {
248                    List<String> ignoreList = CollectionUtils.getTrimmedListFromCSV(ignore);
249                    if (!silent && verbose && !StringUtils.isBlank(ignore)) {
250                            getLog().info("Ignoring " + ignore);
251                    }
252                    return ignoreList;
253            }
254    
255            protected void updateProjectProperties(List<String> ignoreList) throws MojoExecutionException {
256                    Properties projectProperties = project.getProperties();
257                    for (int i = 0; i < locations.length; i++) {
258                            Properties allProperties = utils.getMavenProperties(project);
259                            String originalLocation = locations[i];
260                            String resolvedLocation = utils.getResolvedValue(originalLocation, allProperties);
261                            getLog().debug("o=" + originalLocation + " r=" + resolvedLocation);
262                            if (!validate(resolvedLocation)) {
263                                    continue;
264                            }
265                            if (!silent) {
266                                    getLog().info("Loading " + resolvedLocation);
267                            }
268    
269                            // Load properties from this location
270                            Properties p = getProperties(resolvedLocation);
271    
272                            // Update project properties from the properties we just loaded
273                            updateProperties(projectProperties, p, ignoreList);
274                    }
275            }
276    
277    }