001: package net.sf.invicta.process;
002:
003: import java.io.File;
004: import java.io.FileInputStream;
005: import java.io.FileNotFoundException;
006: import java.io.FileOutputStream;
007: import java.io.IOException;
008: import java.util.HashMap;
009: import java.util.Map;
010: import java.util.Properties;
011:
012: import net.sf.invicta.InvictaConstants;
013: import net.sf.invicta.InvictaException;
014: import net.sf.invicta.Logger;
015:
016: /**
017: * The manager of Invicta configuration properties.
018: * Properties are read from an optional properties file
019: * ('invicta.properties' in default) or as Java system properties.
020: *
021: * This class is used for getting values of properties. It also checks if
022: * the values of properties were changed since the previous Invicta execution.
023: * A temporary output file is used to store the current value of properties.
024: * A setOutputProperty method is available for writing additional values
025: * to the temporary output file (used mainly for last modification time of
026: * files).
027: *
028: **/
029: public class ConfigurationManager {
030: private boolean forceRun = false;
031: private Properties props;
032: private Properties previousProps = new Properties();
033: private Properties outputProps = new Properties();
034: private Map requestedProperties = new HashMap();
035: private String outputPropsFileName;
036:
037: /**
038: *
039: */
040: public ConfigurationManager() throws InvictaException {
041: super ();
042: readProperties();
043: }
044:
045: /**
046: * Sets the value of a property to write to the temporary output file.
047: *
048: * @param propertyName
049: * @param propertyValue
050: */
051: public void setOutputProperty(String propertyName,
052: String propertyValue) throws InvictaProcessException {
053: this .outputProps.setProperty(propertyName, propertyValue);
054: checkPropertyChange(propertyName);
055: }
056:
057: /**
058: * Sets the value of an output property to be the last modification time
059: * of the given file.
060: *
061: * @param propertyPrefix
062: * @param file
063: * @throws InvictaProcessException
064: */
065: public void setOutputTimestampProperty(String propertyPrefix,
066: File file) throws InvictaProcessException {
067: String propertyName = propertyPrefix
068: + InvictaConstants.PROPERTY_SEPARATOR
069: + getPropertyPath(file.getAbsolutePath());
070:
071: String timestamp = String.valueOf(file.lastModified());
072:
073: setOutputProperty(propertyName, timestamp);
074: }
075:
076: /**
077: * Returns the value of a property with the given name.
078: * Throws an exception if this property is not defined.
079: *
080: * @param propertyName
081: * @return String
082: * @throws InvictaProcessException
083: */
084: public String getRequiredProperty(String propertyName)
085: throws InvictaProcessException {
086: String value = getProperty(propertyName);
087:
088: // Check if this property is not defined.
089: if (value == null)
090: throw InvictaProcessException
091: .propertyUndefined(propertyName);
092:
093: return value;
094: }
095:
096: /**
097: * Returns the value of a property with the given name.
098: *
099: * @param propertyName
100: * @return String
101: * @throws InvictaProcessException
102: */
103: public String getProperty(String propertyName)
104: throws InvictaProcessException {
105: // Check if the current getProperty call has a cycle of property referecnes.
106: if (this .requestedProperties.containsKey(propertyName))
107: throw InvictaProcessException
108: .propertyVariableCycle(propertyName);
109:
110: // Add the name of the current property for preventing cycles.
111: this .requestedProperties.put(propertyName, propertyName);
112:
113: // Get the value of a system property with this name.
114: String value = System.getProperty(propertyName);
115:
116: // If the system property is not defined, get the property from the
117: // properties read from the file.
118: if ((value == null) && (this .props != null))
119: value = this .props.getProperty(propertyName);
120:
121: // Parse the value of the property. It's used for replacing
122: // ${propname} string with the value of a property with the name 'propname'.
123: if (value != null) {
124:
125: value = parseProperty(value);
126: // Add the value of the property to the output properties.
127: this .outputProps.setProperty(propertyName, value);
128: }
129:
130: checkPropertyChange(propertyName);
131:
132: // Remove the processed property name from the map of cycle recognition.
133: this .requestedProperties.remove(propertyName);
134: return value;
135: }
136:
137: /**
138: * Get the value of a property from the previous temporary output file.
139: *
140: * @param propertyName
141: * @return String
142: */
143: public String getPreviousProperty(String propertyName)
144: throws InvictaException {
145: return this .previousProps.getProperty(propertyName);
146: }
147:
148: /**
149: * Get the value of a property from the new (future) temporary output
150: * properties.
151: *
152: * @param propertyName
153: * @return String
154: */
155: public String getOutputProperty(String propertyName)
156: throws InvictaException {
157: return this .outputProps.getProperty(propertyName);
158: }
159:
160: /**
161: * Returns whether Invicta should process. Returns true if the property
162: * invicta.force.run is true or if a value of a property (input or
163: * file timestamp) was changed.
164: * @return
165: */
166: public boolean forceRun() {
167: String forceRunProperty = this .props
168: .getProperty(InvictaConstants.PROPERTY_FORCE_RUN);
169: if ((forceRunProperty != null)
170: && (Boolean.valueOf(forceRunProperty).booleanValue()))
171: return true;
172:
173: return this .forceRun;
174: }
175:
176: /**
177: *
178: * @throws InvictaProcessException
179: */
180: public void writeOutputProperties() throws InvictaProcessException {
181: try {
182: this .outputProps
183: .store(new FileOutputStream(
184: this .outputPropsFileName),
185: "Invicta temporary output properties for checking whether Invicta should run.");
186: } catch (Exception e) {
187: throw InvictaProcessException.fileIOError(
188: this .outputPropsFileName, e);
189: }
190: }
191:
192: /**
193: * Convert a file path to a nice looking property key.
194: * @param path
195: * @return
196: */
197: private String getPropertyPath(String path) {
198: path = path.replace('/', '.');
199: path = path.replace('\\', '.');
200: return path;
201: }
202:
203: /**
204: * Reads the input properties file (if exists) and also the previous
205: * temporary output properties file (if exists).
206: *
207: * @throws InvictaProcessException
208: */
209: private void readProperties() throws InvictaProcessException {
210:
211: String propsFile = InvictaConstants.DEFAULT_PROPERTIES_FILE;
212:
213: // Get the name of the properties file.
214: if (System
215: .getProperty(InvictaConstants.PROPERTIES_FILE_NAME_PROPERTY) != null) {
216: propsFile = System
217: .getProperty(InvictaConstants.PROPERTIES_FILE_NAME_PROPERTY);
218: }
219:
220: // Load the input properties
221: this .props = new Properties();
222: try {
223: props.load(new FileInputStream(new File(propsFile)));
224: } catch (FileNotFoundException e) {
225: Logger
226: .warn("Properties file not found (" + propsFile
227: + ")");
228: props = null;
229: } catch (IOException e) {
230: throw InvictaProcessException.fileIOError(propsFile, e);
231: }
232:
233: // Get the name of the temporary output file.
234: String previousPropsFile = InvictaConstants.DEFAULT_OUTPUT_PROPERTIES_FILE;
235: if (System
236: .getProperty(InvictaConstants.OUTPUT_PROPERTIES_FILE_NAME_PROPERTY) != null)
237: previousPropsFile = System
238: .getProperty(InvictaConstants.OUTPUT_PROPERTIES_FILE_NAME_PROPERTY);
239:
240: this .outputPropsFileName = previousPropsFile;
241:
242: // Load the previous output properties from the file.
243: try {
244: previousProps.load(new FileInputStream(new File(
245: previousPropsFile)));
246: } catch (FileNotFoundException e) {
247: Logger
248: .info("Note: Previous output properties file not found. "
249: + "Forcing Invicta execution.");
250: this .forceRun = true;
251: } catch (IOException e) {
252: throw InvictaProcessException.fileIOError(
253: previousPropsFile, e);
254: }
255: }
256:
257: /**
258: * Parse a property value. Replace all variables (${var}) with the
259: * value of a matching property.
260: *
261: * @param value
262: * @return String
263: * @throws InvictaProcessException
264: */
265: private String parseProperty(String value)
266: throws InvictaProcessException {
267: String resolvedValue = "";
268: int varStartPos;
269: int currentStartPos = 0;
270: // Look for variables ('${varname}), and replace the variable with
271: // the value of a property of that variable.
272: while ((varStartPos = value.indexOf(
273: InvictaConstants.PROPERTY_VARIABLE_PREFIX,
274: currentStartPos)) != -1) {
275: // Add to the resolved value the non-variable string.
276: if (varStartPos > currentStartPos)
277: resolvedValue += value.substring(currentStartPos,
278: varStartPos);
279:
280: // Keep the start position of the name.
281: int varNameStartPos = varStartPos
282: + InvictaConstants.PROPERTY_VARIABLE_PREFIX
283: .length();
284:
285: // Find the end position of the variable.
286: int varEndPos = value.indexOf(
287: InvictaConstants.PROPERTY_VARIABLE_SUFFIX,
288: varNameStartPos);
289:
290: // Check if no end character was found.
291: if (varEndPos == -1)
292: throw InvictaProcessException
293: .propertyVariableSyntaxError();
294:
295: // Get the variable name.
296: String varName = value
297: .substring(varNameStartPos, varEndPos);
298:
299: // Get the property value of this variable.
300: String varValue = getRequiredProperty(varName);
301:
302: // Add the property value to the resolved value.
303: resolvedValue += varValue;
304:
305: // Move the current position to be after the replaced variable.
306: currentStartPos = varEndPos + 1;
307: }
308:
309: // Add the rest of the value.
310: if (currentStartPos >= 0)
311: resolvedValue += value.substring(currentStartPos);
312: return resolvedValue;
313: }
314:
315: /**
316: * Set the forceRun flag to true if the value of the given property name
317: * was changed between this and the previous Invicta execution.
318: *
319: * @param propertyName
320: */
321: private void checkPropertyChange(String propertyName) {
322: if (this .forceRun)
323: return;
324:
325: String oldValue = this .previousProps.getProperty(propertyName);
326: String newValue = this .outputProps.getProperty(propertyName);
327: if (!equals(oldValue, newValue)) {
328: Logger.info("Note: Property '" + propertyName
329: + "' was changed. Forcing Invicta execution.");
330: this .forceRun = true;
331: }
332: }
333:
334: /**
335: * Compares two given objects while handling null cases.
336: *
337: * @param o1
338: * @param o2
339: * @return
340: */
341: private boolean equals(Object o1, Object o2) {
342: if ((o1 == null) && (o2 == null))
343: return true;
344: if ((o1 == null) || (o2 == null))
345: return false;
346: return o1.equals(o2);
347: }
348:
349: }
|