KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > inversoft > config > ConfigMediator


1 /*
2  * Copyright (c) 2003, Inversoft
3  *
4  * This software is distribuable under the GNU Lesser General Public License.
5  * For more information visit gnu.org.
6  */

7 package com.inversoft.config;
8
9
10 import java.io.File JavaDoc;
11 import java.io.InputStream JavaDoc;
12 import java.io.Reader JavaDoc;
13 import java.net.URL JavaDoc;
14 import java.util.HashMap JavaDoc;
15 import java.util.Iterator JavaDoc;
16 import java.util.Map JavaDoc;
17
18 import org.apache.log4j.Logger;
19 import org.jdom.Document;
20 import org.jdom.JDOMException;
21 import org.jdom.input.SAXBuilder;
22 import org.xml.sax.InputSource JavaDoc;
23
24 import com.inversoft.error.ErrorList;
25
26
27 /**
28  * <p>
29  * This class is the configuration system mediator that
30  * directs to setu, parsing and building of configuration
31  * from XML documents (which need not be stored in files).
32  * </p>
33  *
34  * <p>
35  * This class can be extended in order to change the order
36  * of operations of change the other parts of the
37  * configuration system.
38  * </p>
39  *
40  * <p>
41  * Prior to building, this mediator creates an instance of
42  * the registry for each factory by calling the {@link
43  * ConfigFactory#createRegistry() createRegistry} method.
44  * These are the registries that are passed to the validate
45  * method later. These are also the registries that are
46  * passed to the commit method at the end of parsing.
47  * </p>
48  *
49  * <p>
50  * This implementation reads each document in order. It first
51  * parses the document into a JDOM Document object. It then
52  * reads the root element of the document. Based on the name
53  * of the root element, a {@link ConfigFactory ConfigFactory}
54  * class is retrieved from the the {@link ConfigFactoryRegistry
55  * ConfigFactoryRegistry} class. This factory is then used
56  * to create a {@link ConfigBuilder ConfigBuilder} instance
57  * that can build the configuration from the document.
58  * </p>
59  *
60  * <p>
61  * When the building happens, this class uses the factory to
62  * build a {@link ConfigRegistry ConfigRegistry} instance.
63  * This registry is the instance that is passed to the
64  * builder during the build process. The factory can return
65  * a null registry, in which case null is passed to the
66  * builder and the registry is ignored.
67  * </p>
68  *
69  * <p>
70  * After all the configuration builders have been called and
71  * all the documents parsed and built, this class calls the
72  * validate method on all the ConfigBuilders it previously
73  * cached. Only the ConfigBuilders which generated NO errors
74  * will be called. This removes the responsibility for
75  * storing the state of the configuration from the
76  * ConfigBuilders and put it in the mediator.
77  * </p>
78  *
79  * <p>
80  * If the the validate method is called, the ConfigRegistry
81  * associated with the ConfigBuilder is passed in, as well
82  * as the local Map of the other Registries.
83  * </p>
84  *
85  * <p>
86  * The ConfigRegistry's are stored in the Map that is passed
87  * to the validate method under the same name as the root
88  * element for which the registry is used during building.
89  * Likewise, this is the key that the ConfigFactory objects
90  * are stored under in the ConfigFactoryRegistry.
91  * </p>
92  *
93  * <p>For example:</p>
94  *
95  * <table>
96  * <tr>
97  * <td>Factory name</td>
98  * <td>MyConfigFactory</td>
99  * </tr>
100  * <tr>
101  * <td>Root Element</td>
102  * <td>MyConfig</td>
103  * </tr>
104  * <tr>
105  * <td>Key of factory in ConfigFactoryRegistry</td>
106  * <td>MyConfig</td>
107  * </tr>
108  * <tr>
109  * <td>Key into Map</td>
110  * <td>MyConfig</td>
111  * </tr>
112  * </table>
113  *
114  * <p>
115  * Rebuilding is done in the same manner as building except
116  * that the {@link ConfigBuilder#rebuild(Document,ConfigRegistry)
117  * rebuild} method is called.
118  * </p>
119  *
120  * <p>
121  * <strong>NOTE:</strong> Only one thread can build or rebuild
122  * at one time. This ensures that multiple calls to build or
123  * rebuild do not interfere with the state of the
124  * ConfigRegistries.
125  * </p>
126  *
127  * @author Brian Pontarelli
128  * @since 2.0
129  * @version 2.0
130  */

131 public class ConfigMediator {
132
133     /**
134      * This classes logger
135      */

136     private static final Logger logger = Logger.getLogger(ConfigMediator.class);
137
138     /**
139      * The SAXBuilder used to parse the documents
140      */

141     protected SAXBuilder saxBuilder;
142
143
144     /**
145      * Constructs a new <code>ConfigMediator</code>.
146      */

147     public ConfigMediator() {
148         saxBuilder = new SAXBuilder();
149     }
150
151
152     /**
153      * <p>
154      * Parse and build the configuration from the documents given.
155      * </p>
156      *
157      * @param documents The JDOM documents to parse and build the configuration
158      * from
159      * @throws ConfigurationException If there were any problems during the
160      * configuration process. All errors that occurred are stored in the
161      * ErrorList object within the exception
162      */

163     public synchronized void mediate(Document[] documents)
164     throws ConfigurationException {
165         rebuild(documents, false);
166     }
167
168     /**
169      * <p>
170      * Parse and build the configuration from the files given.
171      * </p>
172      *
173      * @param files The files to read the XML configuration documents from
174      * @throws ConfigurationException If there were any problems during the
175      * configuration process. All errors that occurred are stored in the
176      * ErrorList object within the exception
177      */

178     public synchronized void mediate(File JavaDoc[] files) throws ConfigurationException {
179         rebuild(files, false);
180     }
181
182     /**
183      * <p>
184      * Parse and build the configuration from the input sources given.
185      * </p>
186      *
187      * @param sources The input sources to read the XML configuration documents
188      * from
189      * @throws ConfigurationException If there were any problems during the
190      * configuration process. All errors that occurred are stored in the
191      * ErrorList object within the exception
192      */

193     public synchronized void mediate(InputSource JavaDoc[] sources)
194     throws ConfigurationException {
195         rebuild(sources, false);
196     }
197
198     /**
199      * <p>
200      * Parse and build the configuration from the input streams given.
201      * </p>
202      *
203      * @param streams The input streams to read the XML configuration documents
204      * from
205      * @throws ConfigurationException If there were any problems during the
206      * configuration process. All errors that occurred are stored in the
207      * ErrorList object within the exception
208      */

209     public synchronized void mediate(InputStream JavaDoc[] streams)
210     throws ConfigurationException {
211         rebuild(streams, false);
212     }
213
214     /**
215      * <p>
216      * Parse and build the configuration from the readers given.
217      * </p>
218      *
219      * @param readers The readers to read the XML configuration documents from
220      * @throws ConfigurationException If there were any problems during the
221      * configuration process. All errors that occurred are stored in the
222      * ErrorList object within the exception
223      */

224     public synchronized void mediate(Reader JavaDoc[] readers)
225     throws ConfigurationException {
226         rebuild(readers, false);
227     }
228
229     /**
230      * <p>
231      * Parse and build the configuration from the urls given.
232      * </p>
233      *
234      * @param urls The urls to read the XML configuration documents from
235      * @throws ConfigurationException If there were any problems during the
236      * configuration process. All errors that occurred are stored in the
237      * ErrorList object within the exception
238      */

239     public synchronized void mediate(URL JavaDoc[] urls)
240     throws ConfigurationException {
241         rebuild(urls, false);
242     }
243
244     /**
245      * <p>
246      * Parse and rebuild the configuration from the documents given.
247      * </p>
248      *
249      * @param documents The JDOM documents to parse and rebuild the configuration
250      * from
251      * @throws ConfigurationException If there were any problems during the
252      * configuration process. All errors that occurred are stored in the
253      * ErrorList object within the exception
254      */

255     public synchronized void rebuild(Document[] documents)
256     throws ConfigurationException {
257         rebuild(documents, true);
258     }
259
260     /**
261      * <p>
262      * Parse and rebuild the configuration from the files given.
263      * </p>
264      *
265      * @param files The files to read the XML configuration documents from
266      * @throws ConfigurationException If there were any problems during the
267      * configuration process. All errors that occurred are stored in the
268      * ErrorList object within the exception
269      */

270     public synchronized void rebuild(File JavaDoc[] files)
271     throws ConfigurationException {
272         rebuild(files, true);
273     }
274
275     /**
276      * <p>
277      * Parse and rebuild the configuration from the input sources given.
278      * </p>
279      *
280      * @param sources The input sources to read the XML configuration documents
281      * from
282      * @throws ConfigurationException If there were any problems during the
283      * configuration process. All errors that occurred are stored in the
284      * ErrorList object within the exception
285      */

286     public synchronized void rebuild(InputSource JavaDoc[] sources)
287     throws ConfigurationException {
288         rebuild(sources, true);
289     }
290
291     /**
292      * <p>
293      * Parse and rebuild the configuration from the input streams given.
294      * </p>
295      *
296      * @param streams The input streams to read the XML configuration documents
297      * from
298      * @throws ConfigurationException If there were any problems during the
299      * configuration process. All errors that occurred are stored in the
300      * ErrorList object within the exception
301      */

302     public synchronized void rebuild(InputStream JavaDoc[] streams)
303     throws ConfigurationException {
304         rebuild(streams, true);
305     }
306
307     /**
308      * <p>
309      * Parse and rebuild the configuration from the readers given.
310      * </p>
311      *
312      * @param readers The readers to read the XML configuration documents from
313      * @throws ConfigurationException If there were any problems during the
314      * configuration process. All errors that occurred are stored in the
315      * ErrorList object within the exception
316      */

317     public synchronized void rebuild(Reader JavaDoc[] readers)
318     throws ConfigurationException {
319         rebuild(readers, true);
320     }
321
322     /**
323      * <p>
324      * Parse and rebuild the configuration from the urls given.
325      * </p>
326      *
327      * @param urls The urls to read the XML configuration documents from
328      * @throws ConfigurationException If there were any problems during the
329      * configuration process. All errors that occurred are stored in the
330      * ErrorList object within the exception
331      */

332     public synchronized void rebuild(URL JavaDoc[] urls) throws ConfigurationException {
333         rebuild(urls, true);
334     }
335
336     /**
337      * Internal method that does the actual work of building or rebuilding the
338      * configuration.
339      */

340     protected synchronized void rebuild(final Object JavaDoc[] sources,
341             final boolean rebuilding)
342     throws ConfigurationException {
343         assert (sources != null && sources.length > 0) :
344             "sources == null || sources.length == 0";
345
346         Map JavaDoc builders = new HashMap JavaDoc();
347         Map JavaDoc registries = new HashMap JavaDoc();
348         Map JavaDoc states = new HashMap JavaDoc();
349         Document document = null;
350         ErrorList errors = new ErrorList();
351
352         // Initialize all the registries and store them locally
353
Map JavaDoc factories = ConfigFactoryRegistry.allFactories();
354         Iterator JavaDoc iter = factories.keySet().iterator();
355         Object JavaDoc key;
356         ConfigRegistry registry;
357         ConfigFactory factory;
358         while (iter.hasNext()) {
359             key = iter.next();
360             factory = (ConfigFactory) factories.get(key);
361             registry = factory.createRegistry();
362             if (registry != null) {
363                 registries.put(key, registry);
364             }
365         }
366
367         // Parse and build/rebuild all the sources
368
for (int i = 0; i < sources.length; i++) {
369             try {
370                 if (sources[i] instanceof Document) {
371                     document = (Document) sources[i];
372                 } else if (sources[i] instanceof File JavaDoc) {
373                     document = saxBuilder.build((File JavaDoc) sources[i]);
374                 } else if (sources[i] instanceof InputSource JavaDoc) {
375                     document = saxBuilder.build((InputSource JavaDoc) sources[i]);
376                 } else if (sources[i] instanceof InputStream JavaDoc) {
377                     document = saxBuilder.build((InputStream JavaDoc) sources[i]);
378                 } else if (sources[i] instanceof Reader JavaDoc) {
379                     document = saxBuilder.build((Reader JavaDoc) sources[i]);
380                 } else if (sources[i] instanceof URL JavaDoc) {
381                     document = saxBuilder.build((URL JavaDoc) sources[i]);
382                 }
383             } catch (JDOMException jdome) {
384                 logger.error(jdome.toString());
385                 continue;
386             }
387
388             rebuild(document, builders, registries, states, rebuilding, errors);
389         }
390
391         // Validate only the builders that have not failed yet
392
validate(builders, registries, states, errors);
393
394         // Should be good to go now and can save the good configuration. The errors
395
// will be thrown next, but good configuration should be stored
396
commit(builders, registries, states);
397
398         if (!errors.isEmpty()) {
399             throw new ConfigurationException(errors);
400         }
401     }
402
403     /**
404      * Locates the factory, if one exists, gets the builder, if one exists, gets
405      * the registry, if one exsists, and calls the build rebuild
406      */

407     protected void rebuild(Document document, Map JavaDoc builders, Map JavaDoc registries,
408             Map JavaDoc states, boolean rebuilding, ErrorList errors) {
409         String JavaDoc rootName = document.getRootElement().getName();
410         ConfigFactory factory = ConfigFactoryRegistry.lookup(rootName);
411
412         if (factory == null) {
413             logger.warn("Unable to locate factory for configuration: " + rootName);
414             errors.addError("Unable to locate factory for configuration: " + rootName);
415             return;
416         }
417
418         ComponentState state = (ComponentState) states.get(rootName);
419         if (state == null) {
420             state = new ComponentState();
421             states.put(rootName, state);
422         }
423
424         ConfigBuilder builder = getBuilder(factory, rootName, builders);
425         ConfigRegistry registry = (ConfigRegistry) registries.get(rootName);
426
427         try {
428             if (rebuilding) {
429                 builder.rebuild(document, registry);
430             } else {
431                 builder.build(document, registry);
432             }
433         } catch (ConfigurationException ce) {
434             errors.addErrorList(ce.getErrors());
435             state.valid = false;
436         }
437     }
438
439     /**
440      * Checks the local cache for the build under the rootName given. If it does not
441      * exist, uses the factory to create a new one and caches it.
442      */

443     protected ConfigBuilder getBuilder(ConfigFactory factory, String JavaDoc rootName,
444             Map JavaDoc builders) {
445
446         ConfigBuilder builder = (ConfigBuilder) builders.get(rootName);
447         if (builder == null) {
448             builder = factory.createBuilder();
449             assert (builder != null) : "builder == null";
450             builders.put(rootName, builder);
451         }
452
453         return builder;
454     }
455
456     /**
457      * Calls the validate method on all the factory instance that were cached
458      * locally during the configuration process. This passes the local cache of
459      * registry instances to the validate method.
460      */

461     protected void validate(Map JavaDoc builders, Map JavaDoc registries, Map JavaDoc states,
462             ErrorList errors) {
463
464         Iterator JavaDoc iter = builders.entrySet().iterator();
465         Map.Entry JavaDoc entry;
466         ConfigBuilder builder;
467         ConfigRegistry registry;
468         String JavaDoc rootName;
469         Map JavaDoc tempRegistries;
470         ComponentState state;
471         while (iter.hasNext()) {
472             entry = (Map.Entry JavaDoc) iter.next();
473             builder = (ConfigBuilder) entry.getValue();
474             rootName = (String JavaDoc) entry.getKey();
475
476             state = (ComponentState) states.get(rootName);
477             if (!state.valid) {
478                 continue;
479             }
480
481             registry = (ConfigRegistry) registries.get(rootName);
482             tempRegistries = new HashMap JavaDoc(registries);
483             tempRegistries.remove(rootName);
484
485             try {
486                 builder.validate(registry, tempRegistries);
487             } catch (ConfigurationException ce) {
488                 errors.addErrorList(ce.getErrors());
489                 state.valid = false;
490             }
491         }
492     }
493
494     /**
495      * Calls the validate method on all the factory instance that were cached
496      * locally during the configuration process. This passes the local cache of
497      * registry instances to the validate method.
498      */

499     protected void commit(Map JavaDoc builders, Map JavaDoc registries, Map JavaDoc states) {
500
501         Iterator JavaDoc iter = builders.entrySet().iterator();
502         Map.Entry JavaDoc entry;
503         ConfigBuilder builder;
504         ConfigRegistry registry;
505         String JavaDoc rootName;
506         Map JavaDoc tempRegistries;
507         ComponentState state;
508         while (iter.hasNext()) {
509             entry = (Map.Entry JavaDoc) iter.next();
510             builder = (ConfigBuilder) entry.getValue();
511             rootName = (String JavaDoc) entry.getKey();
512
513             state = (ComponentState) states.get(rootName);
514             if (!state.valid) {
515                 continue;
516             }
517
518             registry = (ConfigRegistry) registries.get(rootName);
519             tempRegistries = new HashMap JavaDoc(registries);
520             tempRegistries.remove(rootName);
521
522             builder.commit(registry, tempRegistries);
523         }
524     }
525
526
527     /**
528      * This class stores the state for a single configuration
529      * component. Each component has a single state object
530      * associated with it. If during the process of mediation a
531      * component configuration is invalid, than the state for
532      * that component will be set to invalid.
533      */

534     public class ComponentState {
535         public boolean valid = true;
536     }
537 }
Popular Tags