001: /********************************************************************************
002: * CruiseControl, a Continuous Integration Toolkit
003: * Copyright (c) 2007, ThoughtWorks, Inc.
004: * 200 E. Randolph, 25th Floor
005: * Chicago, IL 60601 USA
006: * All rights reserved.
007: *
008: * Redistribution and use in source and binary forms, with or without
009: * modification, are permitted provided that the following conditions
010: * are met:
011: *
012: * + Redistributions of source code must retain the above copyright
013: * notice, this list of conditions and the following disclaimer.
014: *
015: * + Redistributions in binary form must reproduce the above
016: * copyright notice, this list of conditions and the following
017: * disclaimer in the documentation and/or other materials provided
018: * with the distribution.
019: *
020: * + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
021: * names of its contributors may be used to endorse or promote
022: * products derived from this software without specific prior
023: * written permission.
024: *
025: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
026: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
027: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
028: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
029: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
030: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
031: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
032: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
033: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
034: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
035: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
036: ********************************************************************************/package net.sourceforge.cruisecontrol.config;
037:
038: import net.sourceforge.cruisecontrol.util.ValidationHelper;
039: import net.sourceforge.cruisecontrol.util.OSEnvironment;
040: import net.sourceforge.cruisecontrol.ProjectXMLHelper;
041: import net.sourceforge.cruisecontrol.CruiseControlException;
042:
043: import java.util.Map;
044: import java.util.Iterator;
045: import java.io.File;
046: import java.io.FileReader;
047: import java.io.BufferedReader;
048: import java.io.IOException;
049: import java.io.FileNotFoundException;
050:
051: /**
052: * <p>The <code><property></code> element is used to set a property (or set of properties)
053: * within the CruiseControl configuration file. Properties may be set at the global level
054: * and/or within the scope of a project. There are three ways to set properties within CruiseControl:</p>
055: *
056: * <ol>
057: * <li>By supplying both the name and value attributes.</li>
058: * <li>By setting the file attribute with the filename of the property file to load.
059: * This property file must follow the format defined by the class java.util.Properties,
060: * with the same rules about how non-ISO8859-1 characters must be escaped.</li>
061: * <li>By setting the environment attribute with a prefix to use. Properties will be defined
062: * for every environment variable by prefixing the supplied name and a period to the name
063: * of the variable.</li>
064: *
065: * </ol>
066: *
067: * <p>Properties in CruiseControl are <i>not entirely</i> immutable: whoever sets a property <i>last</i>
068: * will freeze it's value <i>within the scope in which the property was set</i>. In other words,
069: * you may define a property at the global level, then eclipse this value within the scope of a single
070: * project by redefining the property within that project. You may not, however, set a property more
071: * than once within the same scope. If you do so, only the last assignment will be used.</p>
072: *
073: * <p>Just as in Ant, the value part of a property being set may contain references to other properties.
074: * These references are resolved at the time these properties are set. This also holds for properties
075: * loaded from a property file, or from the environment.</p>
076: *
077: * <p>Also note that the property <code>${project.name}</code> is set for you automatically and will always resolve
078: * to the name of the project currently being serviced - even outside the scope of the project
079: * definition.</p>
080: *
081: * <p>Finally, note that properties bring their best when combined with
082: * <a href=\"plugins.html#preconfiguration\">plugin preconfigurations</a>.
083: * </p>
084: */
085: public class DefaultPropertiesPlugin implements PropertiesPlugin {
086: private String file;
087: private String environment;
088: private String name;
089: private String value;
090: private String toupper;
091:
092: /**
093: * The name of the property to set.
094: * @required Exactly one of name, environment, or file.
095: */
096: public void setName(String name) {
097: this .name = name;
098: }
099:
100: /**
101: * The prefix to use when retrieving environment variables.
102: * Thus if you specify environment="myenv" you will be able to access OS-specific environment variables
103: * via property names such as "myenv.PATH" or "myenv.MAVEN_HOME".
104: * @required Exactly one of name, environment, or file.
105: */
106: public void setEnvironment(String environment) {
107: this .environment = environment;
108: }
109:
110: /**
111: * The filename of the property file to load.
112: * @required Exactly one of name, environment, or file.
113: */
114: public void setFile(String file) {
115: this .file = file;
116: }
117:
118: /**
119: *
120: * @param value
121: * @required Yes, if name was set.
122: */
123: public void setValue(String value) {
124: this .value = value;
125: }
126:
127: /**
128: * Used in conjunction with <code>environment</code>. If set to <code>true</code>, all
129: * environment variable names will be converted to upper case.
130: * @param toupper
131: */
132: public void setToupper(String toupper) {
133: this .toupper = toupper;
134: }
135:
136: /**
137: * Called after the configuration is read to make sure that all the mandatory parameters were specified.. @throws
138: * CruiseControlException if there was a configuration error.
139: */
140: public void validate() throws CruiseControlException {
141: if (name == null && file == null && environment == null) {
142: ValidationHelper
143: .fail("At least one of name, file or environment must be set.");
144: }
145: if ((name != null
146: && (file != null || environment != null)
147: || (file != null && (name != null || environment != null)) || (environment != null && (file != null || name != null)))) {
148: ValidationHelper
149: .fail("At most one of name, file or environment can be set.");
150: }
151:
152: if (file != null && file.trim().length() > 0) {
153: // TODO FIXME add exists check.
154: }
155: if (name != null && value == null) {
156: ValidationHelper
157: .fail("name and value must be set simultaneoulsy.");
158: }
159: }
160:
161: public void loadProperties(Map props, boolean failIfMissing)
162: throws CruiseControlException {
163: boolean toUpperValue = "true".equals(toupper);
164: if (file != null && file.trim().length() > 0) {
165: File theFile = new File(this .file);
166: // TODO FIXME add exists check.
167: try {
168: BufferedReader reader = new BufferedReader(
169: new FileReader(theFile));
170: // Read the theFile line by line, expanding macros
171: // as we go. We must do this manually to preserve the
172: // order of the properties.
173: String line;
174: while ((line = reader.readLine()) != null) {
175: line = line.trim();
176: if (line.length() == 0 || line.charAt(0) == '#') {
177: continue;
178: }
179: int index = line.indexOf('=');
180: if (index < 0) {
181: continue;
182: }
183: String parsedName = ProjectXMLHelper
184: .parsePropertiesInString(props, line
185: .substring(0, index).trim(),
186: failIfMissing);
187: String parsedValue = ProjectXMLHelper
188: .parsePropertiesInString(props, line
189: .substring(index + 1).trim(),
190: failIfMissing);
191: ProjectXMLHelper.setProperty(props, parsedName,
192: parsedValue);
193: }
194: reader.close();
195: } catch (FileNotFoundException e) {
196: throw new CruiseControlException(
197: "Could not load properties from theFile \""
198: + this .file
199: + "\". The theFile does not exist", e);
200: } catch (IOException e) {
201: throw new CruiseControlException(
202: "Could not load properties from theFile \""
203: + this .file + "\".", e);
204: }
205: } else if (environment != null) {
206: // Load the environment into the project's properties
207: Iterator variables = new OSEnvironment().getEnvironment()
208: .iterator();
209: while (variables.hasNext()) {
210: String line = (String) variables.next();
211: int index = line.indexOf('=');
212: if (index < 0) {
213: continue;
214: }
215: // If the toupper attribute was set, upcase the variables
216: StringBuffer propName = new StringBuffer(environment);
217: propName.append(".");
218: if (toUpperValue) {
219: propName.append(line.substring(0, index)
220: .toUpperCase());
221: } else {
222: propName.append(line.substring(0, index));
223: }
224: String parsedValue = ProjectXMLHelper
225: .parsePropertiesInString(props, line
226: .substring(index + 1), failIfMissing);
227: ProjectXMLHelper.setProperty(props,
228: propName.toString(), parsedValue);
229: }
230: } else {
231: String parsedValue = ProjectXMLHelper
232: .parsePropertiesInString(props, value,
233: failIfMissing);
234: ProjectXMLHelper.setProperty(props, name, parsedValue);
235: }
236: }
237: }
|