001: package org.antmod.conf;
002:
003: import java.io.File;
004: import java.io.IOException;
005: import java.text.ParseException;
006: import java.util.ArrayList;
007: import java.util.Enumeration;
008: import java.util.Hashtable;
009: import java.util.Iterator;
010: import java.util.Properties;
011: import java.util.logging.Logger;
012:
013: import org.antmod.buildplugin.BuildPluginFactory;
014: import org.antmod.scm.ScmSystem;
015: import org.antmod.scm.ScmSystemFactory;
016: import org.antmod.scm.ScmUrl;
017: import org.antmod.util.Os;
018: import org.apache.commons.io.FileUtils;
019: import org.apache.commons.lang.BooleanUtils;
020: import org.apache.commons.lang.SystemUtils;
021: import org.apache.tools.ant.Project;
022: import org.apache.tools.ant.taskdefs.Property;
023:
024: /**
025: * Accesses the proper set of Antmod properties, reading
026: * all default and user specific config files in the proper order.
027: *
028: * @author Klaas Waslander
029: */
030: public final class AntmodProperties {
031: private final static Logger LOGGER = Logger
032: .getLogger(AntmodProperties.class.getName());
033:
034: private final static Properties PROPS = new Properties();
035: static {
036: loadAntmodProperties(PROPS);
037: }
038:
039: /** never to be instantiated */
040: private AntmodProperties() {
041: }
042:
043: /**
044: * Get the property with the given name, first checking user specific properties,
045: * and then the globally defined default property values.
046: * @param propertyName The name of the antmod property to retrieve.
047: * @return The value of the property if set.
048: */
049: public static String getProperty(String propertyName) {
050: return PROPS.getProperty(propertyName);
051: }
052:
053: public static boolean getBoolean(String propertyName) {
054: String propVal = getProperty(propertyName);
055: return propVal != null
056: && (propVal.trim().equalsIgnoreCase("true") || propVal
057: .trim().equalsIgnoreCase("yes"));
058: }
059:
060: /**
061: * Get all property names starting with the given string.
062: * @param startOfPropertyName The string that each property name should start with, case sensitive!
063: * @return An iterator with String objects, being the property names that are valid.
064: */
065: public static Iterator getPropertyNamesStartingWith(
066: String startOfPropertyName) {
067: ArrayList result = new ArrayList();
068: Enumeration propNames = PROPS.propertyNames();
069: String propName;
070: while (propNames.hasMoreElements()) {
071: propName = (String) propNames.nextElement();
072: if (propName.startsWith(startOfPropertyName)) {
073: result.add(propName);
074: }
075: }
076: return result.iterator();
077: }
078:
079: /**
080: * Load the antmod configuration properties into the given ant project.
081: * @param antProject The Ant project into which the config is to be loaded
082: */
083: public static void loadIntoAnt(Project antProject) {
084: Enumeration propNames = PROPS.propertyNames();
085: String propName;
086: while (propNames.hasMoreElements()) {
087: propName = (String) propNames.nextElement();
088: antProject.setProperty(propName, PROPS
089: .getProperty(propName));
090: }
091: }
092:
093: /** Internal utility for loading properties from both global and user specific props files */
094: private static void loadAntmodProperties(Properties antmodProperties) {
095: String antmodHome = Os.getEnvironmentVariable("ANTMOD_HOME");
096:
097: Project localAntProject = new Project();
098: localAntProject.init();
099:
100: Property antProp = new Property();
101: antProp.setProject(localAntProject);
102: antProp.setEnvironment("env");
103: antProp.execute();
104:
105: // user configuration properties
106: antProp = new Property();
107: antProp.setProject(localAntProject);
108: antProp.setFile(new File(SystemUtils.USER_HOME, ".antmodrc"));
109: antProp.execute();
110:
111: // backwards compatibility
112: antProp = new Property();
113: antProp.setProject(localAntProject);
114: antProp.setFile(new File(SystemUtils.USER_HOME,
115: ".antmod.properties"));
116: antProp.execute();
117:
118: // backwards compatibility
119: antProp = new Property();
120: antProp.setProject(localAntProject);
121: antProp.setFile(new File(SystemUtils.USER_HOME,
122: "local.antmod.properties"));
123: antProp.execute();
124:
125: // default Antmod configuration properties
126: antProp = new Property();
127: antProp.setProject(localAntProject);
128: antProp.setFile(new File(antmodHome, "resources"
129: + File.separator + "conf" + File.separator
130: + "antmod.properties"));
131: antProp.execute();
132:
133: PROPS.putAll(localAntProject.getProperties());
134:
135: // plugin property files
136: File[] validPluginHomes = BuildPluginFactory
137: .getValidPluginHomes();
138: File pluginPropsFile;
139: for (int i = validPluginHomes.length; i-- > 0;) {
140: pluginPropsFile = new File(validPluginHomes[i],
141: "plugin.properties");
142: if (pluginPropsFile.exists()) {
143: antProp = new Property();
144: antProp.setProject(localAntProject);
145: antProp.setFile(pluginPropsFile);
146: antProp.execute();
147: }
148: }
149: PROPS.putAll(localAntProject.getProperties());
150:
151: //
152: // Add centralized configuration from scm
153: //
154: ScmUrl confUrl = null;
155: try {
156: if (BooleanUtils.toBoolean(localAntProject
157: .getProperty("antmod.conf.enabled"))) {
158: confUrl = new ScmUrl(localAntProject
159: .getProperty("antmod.conf.repos.url"));
160: }
161: } catch (IllegalArgumentException e) {
162: //e.printStackTrace();
163: } catch (ParseException e) {
164: e.printStackTrace();
165: }
166: if (confUrl != null) {
167: // update locally cached config
168: ScmSystem confScm = ScmSystemFactory
169: .getScmSystemByUrl(confUrl);
170: File localConfDir = new File(localAntProject
171: .getProperty("antmod.conf.localdir"));
172: File localDir = new File(localAntProject
173: .getProperty("antmod.local.dir"));
174: localConfDir.mkdirs();
175:
176: // only checkout or update periodically
177: checkoutIfNeeded(confScm, localConfDir, localDir);
178:
179: // load properties into empty ant project
180: Project confAntProject = new Project();
181: confAntProject.init();
182: //int initialPropertyCount = confAntProject.getProperties().size();
183:
184: // iterate across all files in the conf directory, and load into ant project
185: File[] confFiles = localConfDir.listFiles();
186: if (confFiles != null && confFiles.length > 0) {
187: File confFile;
188: for (int i = 0; i < confFiles.length; i++) {
189: confFile = confFiles[i];
190: if (!confFile.isDirectory()) {
191: antProp = new Property();
192: antProp.setProject(confAntProject);
193: antProp.setFile(confFile);
194: antProp.execute();
195: }
196: }
197:
198: // override already loaded properties
199: // iterate across properties, and replace variable references with value of variables already loaded
200: Hashtable confProps = confAntProject.getProperties();
201: Iterator confKeys = confProps.keySet().iterator();
202: while (confKeys.hasNext()) {
203: String confKey = (String) confKeys.next();
204:
205: // replace "${}" constructs left in the property value with values in local config
206: String confVal = localAntProject
207: .replaceProperties((String) confProps
208: .get(confKey));
209: PROPS.put(confKey, confVal);
210: }
211:
212: } else {
213: //System.err.println("NO centralized antmod config found in SCM");
214: }
215: }
216: }
217:
218: private static void checkoutIfNeeded(final ScmSystem confScm,
219: final File localConfDir, File localDir) {
220: // check last modified of temp file
221: localDir.mkdirs();
222: File lastModifiedFile = new File(localDir, localConfDir
223: .getName()
224: + ".lastcheckout");
225: long lastModified = 0;
226: if (lastModifiedFile.exists()) {
227: lastModified = lastModifiedFile.lastModified();
228: }
229:
230: if (!confScm.isCheckoutDir(localConfDir)
231: || System.currentTimeMillis() - lastModified > Integer
232: .parseInt(getProperty("antmod.conf.checkoutinterval.minutes")) * 60000) {
233: // more than one hour passed!
234: System.err
235: .println("antmod.conf.enabled=true : retrieving 'antmod-conf' module");
236:
237: if (!confScm.isCheckoutDir(localConfDir)) {
238: // the first time a checkout is attempted, always block until it completes
239: // such that the invocating code can immediately load the centralized config parameters
240: confScm.doCheckoutOrUpdate(
241: confScm.getUrl().getModule(), localConfDir,
242: null, true);
243: } else {
244: Thread backgroundCheckout = new Thread() {
245: public void run() {
246: confScm.doCheckoutOrUpdate(confScm.getUrl()
247: .getModule(), localConfDir, null, true);
248: }
249: };
250: backgroundCheckout.setPriority(Thread.MAX_PRIORITY);
251: backgroundCheckout.start();
252: }
253:
254: /* PROBLEM: if main thread sleeps, the backgroundCheckout thread cannot start subprocess for svn/cvs...
255: try {
256: int secondsWait = Integer.parseInt(getProperty("antmod.conf.checkoutwait.seconds"));
257: Thread.sleep(secondsWait * 1000);
258: if (backgroundCheckout.isAlive()) {
259: System.err.println("- check NOT completed, you are probably working offline -");
260: }
261: }
262: catch (InterruptedException e1) {
263: e1.printStackTrace();
264: }
265: */
266:
267: // (re-)create lastmodified file
268: if (lastModifiedFile.exists()) {
269: lastModifiedFile.delete();
270: }
271: try {
272: FileUtils
273: .writeStringToFile(
274: lastModifiedFile,
275: "This file is used for seeing when the centralized antmod-conf module was last checked out / updated.",
276: SystemUtils.FILE_ENCODING);
277: } catch (IOException e) {
278: e.printStackTrace();
279: }
280: }
281: }
282: }
|