1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package nl.toolforge.karma.core.boot;
20
21 import nl.toolforge.karma.core.ErrorCode;
22 import nl.toolforge.karma.core.location.Location;
23 import nl.toolforge.karma.core.location.LocationDescriptor;
24 import nl.toolforge.karma.core.location.LocationException;
25 import nl.toolforge.karma.core.location.LocationFactory;
26 import org.apache.commons.digester.Digester;
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.xml.sax.SAXException;
30
31 import java.io.BufferedWriter;
32 import java.io.File;
33 import java.io.FileWriter;
34 import java.io.IOException;
35 import java.io.Writer;
36 import java.text.MessageFormat;
37 import java.util.ArrayList;
38 import java.util.Enumeration;
39 import java.util.Iterator;
40 import java.util.List;
41 import java.util.Properties;
42
43 /***
44 * <p>Configuration class for a <code>WorkingContext</code>. A <code>WorkingContextConfiguration</code> consists of the
45 * following configuration:
46 *
47 * <ul>
48 * <li>Properties that can be used by Karma. Three properties are mandatory :
49 * <ul>
50 * <li>
51 * <code>project.baseDir</code>, indicating the directory where projects for a working context are stored.
52 * </li>
53 * <li>
54 * <code>manifest-store.module</code>, which is the module name for the manifest store in a version control
55 * system. Note that the format of the module name might be version control system dependent.
56 * </li>
57 * <li>
58 * <code>location-store.module</code>, which is the module name for the location store in a version control
59 * system. Note that the format of the module name might be version control system dependent.
60 * </li>
61 * </ul>
62 * <li/>
63 * <li>A manifest store <code>Location</code>, describing the version control system where the
64 * <code>manifest-store.module</code> module can be found.
65 * <li/>
66 * <li>A location store <code>Location</code>, describing the version control system where the
67 * <code>location-store.module</code> module can be found.
68 * <li/>
69 * </ul>
70 *
71 * <p>The configuration is not loaded automatically. By calling the {@link #load()}-method the configuration will be
72 * loaded.
73 *
74 * @author D.A. Smedes
75 * @version $Id: WorkingContextConfiguration.java,v 1.6 2004/11/03 20:54:14 asmedes Exp $
76 */
77 public final class WorkingContextConfiguration {
78
79 public static final ErrorCode CONFIGURATION_LOAD_ERROR = new ErrorCode("WCC-00001");
80
81 private static Log logger = LogFactory.getLog(WorkingContextConfiguration.class);
82
83
84
85 private ManifestStore manifestStore = null;
86 private LocationStore locationStore = null;
87
88 private Properties configuration = null;
89
90 private WorkingContext workingContext = null;
91
92
93
94 /***
95 * Creates a configuration object using <code>configFile</code> as the configuration file. The configuration is
96 * loaded by calling {@link #load()}.
97 *
98 * @param workingContext The <code>WorkingContext</code> for this configuration.
99 */
100 public WorkingContextConfiguration(WorkingContext workingContext) {
101
102 if (workingContext == null) {
103 throw new IllegalArgumentException("Working context cannot be null.");
104 }
105
106 this.workingContext = workingContext;
107 configuration = new Properties();
108 }
109
110 private File getConfigFile() {
111 return new File(workingContext.getWorkingContextConfigurationBaseDir(), "working-context.xml");
112 }
113
114 /***
115 * Returns the <code>LocationStore</code> for this configuration. Can be <code>null</code> if not configured properly.
116 *
117 * @return The <code>LocationStore</code> for this configuration. Can be <code>null</code> if not configured properly.
118 */
119 public LocationStore getLocationStore() {
120 return locationStore;
121 }
122
123 /***
124 * Returns the <code>ManifestStore</code> for this configuration. Can be <code>null</code> if not configured properly.
125 *
126 * @return The <code>ManifestStore</code> for this configuration. Can be <code>null</code> if not configured properly.
127 */
128 public ManifestStore getManifestStore() {
129 return manifestStore;
130 }
131
132 /***
133 * Sets the manifest store for this configuration. <code>null</code>s are allowed.
134 *
135 * @param manifestStore The manifest store for this configuration.
136 */
137 public void setManifestStore(ManifestStore manifestStore) {
138 this.manifestStore = manifestStore;
139 }
140
141 /***
142 * Sets the location store for this configuration. <code>null</code>s are allowed.
143 *
144 * @param locationStore The location store for this configuration.
145 */
146 public void setLocationStore(LocationStore locationStore) {
147 this.locationStore = locationStore;
148 }
149
150
151 /***
152 *
153 * @param key
154 * @return <code>null</code> if the property is not found.
155 */
156 public String getProperty(String key) {
157
158 if (configuration == null) {
159 return null;
160 }
161
162 return configuration.getProperty(key);
163 }
164
165
166
167 /***
168 * Adds or changes a property in the current configuration. When a property with key <code>name</code> exists, its
169 * value is overwritten with <code>value</code>.
170 */
171 public void setProperty(String name, String value) {
172 configuration.setProperty(name, value);
173 }
174
175
176
177 /***
178 * Thorough checks of this configuration is valid, and if it is not, returns the <code>ErrorCode</code> to indicate
179 * what went wrong or <code>null</code> if nothing went wrong, and this configuration is ready to use.
180 *
181 * @return An <code>ErrorCode</code> indicating the exact failure or <code>null</code> of nothing it wrong.
182 */
183 public ErrorCode check() {
184
185 try {
186 if (load()) {
187
188 }
189 } catch (WorkingContextException e) {
190 return CONFIGURATION_LOAD_ERROR;
191 }
192
193 return null;
194 }
195
196
197
198
199
200 /***
201 * <p>Loads the configuration from <code>working-context.xml</code>. When the file did not exists, <code>false</code>
202 * will be returned. Otherwise, this method returns <code>true</code> and the configuration succeeded. Note that
203 * this method performs no validation on the configuration itself. This is left to the user.
204 *
205 * <p>When the configuration could be loaded, this method returns <code>true</code>. This is not to say that the
206 * configuration is correct. The configuration should still be checked for correctness by the client.
207 *
208 * <p>Calling this method overwrites any properties already set for this configuration.
209 *
210 * @return <code>true</code> when the configuration could be loaded, <code>false</code> if it
211 * couldn't.
212 *
213 * @throws WorkingContextException When an <code>IOException</code> or <code>SAXException</code> occurs, indicating
214 * an error when reading a configuration file, which could result by the client in
215 * specific actions (thus the exception and not the <code>true</code> or
216 * <code>false</code>.
217 */
218 public boolean load() throws WorkingContextException {
219
220 configuration = new Properties();
221
222
223
224 Digester propertyDigester = getPropertyDigester();
225
226 List properties = null;
227
228 try {
229 properties = (List) propertyDigester.parse(getConfigFile());
230 } catch (SAXException e) {
231 logger.error(e);
232 throw new WorkingContextException("XML error in configuration for working context `" + workingContext + "`. ", e);
233 } catch (IOException e) {
234 logger.error(e);
235 throw new WorkingContextException(e);
236 }
237
238 for (Iterator i = properties.iterator(); i.hasNext();) {
239 Property property = (Property) i.next();
240 configuration.put(property.getName(), property.getValue());
241 }
242
243
244
245 Digester locationDigester = LocationDescriptor.getDigester();
246
247 List locations = null;
248
249 try {
250 locations = (List) locationDigester.parse(getConfigFile());
251 } catch (SAXException e) {
252 logger.error(e);
253 throw new WorkingContextException("XML error in configuration for working context `" + workingContext + "`. ", e);
254 } catch (IOException e) {
255 logger.error(e);
256 throw new WorkingContextException(e);
257 }
258
259 if (locations == null) {
260 return false;
261 }
262
263
264
265 for (Iterator i = locations.iterator(); i.hasNext();) {
266
267 LocationDescriptor descriptor = (LocationDescriptor) i.next();
268
269 Location location = null;
270 try {
271 location = LocationFactory.getInstance().createLocation(descriptor);
272 } catch (LocationException e) {
273 return false;
274 }
275
276 location.setWorkingContext(workingContext);
277
278 if ("manifest-store".equals(location.getId())) {
279
280 String moduleName = (String) configuration.get(WorkingContext.MANIFEST_STORE_MODULE);
281 manifestStore = new ManifestStore(workingContext, moduleName, location);
282
283 } else if ("location-store".equals(location.getId())) {
284
285 String moduleName = (String) configuration.get(WorkingContext.LOCATION_STORE_MODULE);
286 locationStore = new LocationStore(workingContext, moduleName, location);
287
288 } else {
289 logger.error(
290 "Invalid location element in `working-context.xml`. " +
291 "Expecting a `manifest-store` and `location-store` entry.");
292 return false;
293 }
294 }
295
296 if (manifestStore == null || locationStore == null) {
297 return false;
298 }
299
300 return true;
301 }
302
303 /***
304 * Stores the all configuration items in <code>configuration</code> in the <code>working-context.xml</code> file.
305 *
306 * @throws WorkingContextException When storing failed.
307 */
308 public void store() throws WorkingContextException {
309
310
311
312 if (configuration == null) {
313 throw new NullPointerException("Configuration cannot be null.");
314 }
315
316
317
318
319 StringBuffer buffer = new StringBuffer();
320 buffer.append("<?xml version=\"1.0\"?>\n");
321
322 buffer.append(
323 "<wc:working-context\n" +
324 " xmlns:wc=\"http://www.toolforge.org/specifications/working-context\"\n" +
325 " xmlns:loc=\"http://www.toolforge.org/specifications/location\">\n");
326
327
328
329 buffer.append(" <wc:properties>\n");
330
331 MessageFormat formatter = new MessageFormat(" <wc:property name=\"{0}\" value=\"{1}\"/>\n");
332
333 Enumeration e = configuration.propertyNames();
334
335 while (e.hasMoreElements()) {
336
337 String key = (String) e.nextElement();
338 String value = configuration.getProperty(key);
339
340 buffer.append(formatter.format(new String[]{key, value}));
341 }
342
343 buffer.append(" </wc:properties>\n\n");
344
345
346
347 if (manifestStore == null && locationStore == null) {
348
349 } else {
350
351 buffer.append(" <loc:locations>\n");
352
353 if (manifestStore != null) {
354 buffer.append(manifestStore.getLocation().asXML());
355 }
356 if (locationStore != null) {
357 buffer.append(locationStore.getLocation().asXML());
358 }
359
360 buffer.append(" </loc:locations>\n");
361
362 }
363
364 buffer.append("\n");
365
366 buffer.append("</wc:working-context>\n");
367
368
369
370 try {
371 if (!getConfigFile().exists()) {
372 getConfigFile().createNewFile();
373 }
374 Writer writer = new BufferedWriter(new FileWriter(getConfigFile()));
375 writer.write(buffer.toString());
376 writer.flush();
377 } catch (IOException ioe) {
378 logger.error(ioe);
379 throw new WorkingContextException(ioe);
380 }
381 }
382
383
384
385
386
387
388
389
390
391
392 private Digester getPropertyDigester() {
393
394 Digester digester = new Digester();
395
396 digester.setNamespaceAware(true);
397
398 digester.addObjectCreate("working-context/properties", ArrayList.class);
399
400 digester.addObjectCreate("working-context/properties/property", Property.class);
401 digester.addSetProperties("working-context/properties/property");
402 digester.addSetNext("working-context/properties/property", "add");
403
404 return digester;
405 }
406 }