KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > sape > carbon > core > config > DefaultRootConfigurationService


1 /*
2  * The contents of this file are subject to the Sapient Public License
3  * Version 1.0 (the "License"); you may not use this file except in compliance
4  * with the License. You may obtain a copy of the License at
5  * http://carbon.sf.net/License.html.
6  *
7  * Software distributed under the License is distributed on an "AS IS" basis,
8  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
9  * the specific language governing rights and limitations under the License.
10  *
11  * The Original Code is The Carbon Component Framework.
12  *
13  * The Initial Developer of the Original Code is Sapient Corporation
14  *
15  * Copyright (C) 2003 Sapient Corporation. All Rights Reserved.
16  */

17
18 package org.sape.carbon.core.config;
19
20 import java.util.StringTokenizer JavaDoc;
21
22 import org.sape.carbon.core.config.cache.ConfigurationCache;
23 import org.sape.carbon.core.config.cache.ConfigurationCacheFactory;
24 import org.sape.carbon.core.config.format.ConfigurationFormatException;
25 import org.sape.carbon.core.config.format.ConfigurationFormatService;
26 import org.sape.carbon.core.config.format.DefaultConfigurationFormatService;
27 import org.sape.carbon.core.config.node.ConfigurationDocument;
28 import org.sape.carbon.core.config.node.Folder;
29 import org.sape.carbon.core.config.node.Node;
30 import org.sape.carbon.core.config.node.NodeCreationException;
31 import org.sape.carbon.core.config.node.NodeIOException;
32 import org.sape.carbon.core.config.node.NodeNotFoundException;
33 import org.sape.carbon.core.config.node.event.NodeEventListener;
34 import org.sape.carbon.core.config.node.link.LinkNodeConfiguration;
35 import org.sape.carbon.core.config.node.link.LinkNodeFactory;
36 import org.sape.carbon.core.exception.IllegalStateException;
37 import org.sape.carbon.core.exception.InvalidParameterException;
38
39 import org.apache.commons.logging.Log;
40 import org.apache.commons.logging.LogFactory;
41
42 /**
43  * Implementation of <code>RootConfigurationService</code> that traverses
44  * the node tree to fetch and store configuration data.
45  *
46  * Copyright 2001 Sapient
47  * @since carbon 1.0
48  * @author Greg Hinkle, December 2001
49  * @version $Revision: 1.42 $($Author: dvoet $ / $Date: 2003/05/05 21:21:15 $)
50  */

51 public class DefaultRootConfigurationService
52     implements ConfigurationService {
53
54     /**
55      * Provides a handle to Apache-commons logger
56      */

57     private Log log =
58         LogFactory.getLog(this.getClass());
59
60     /**
61      * Handle to the root node used by this <code>ConfigurationService</code>.
62      */

63     private Node rootNode = null;
64
65     /**
66      * Cache storing previously accessed configuration objects.
67      * @since carbon 1.1
68      */

69     private ConfigurationCache cache;
70
71     /**
72      * protected constructor prevents direct instantiation by classes other
73      * than the <code>DefaultConfigurationServiceFactory</code> but does not
74      * prevent extension.
75      *
76      * @param configurationDocument the configuration document being
77      * loaded
78      */

79     protected DefaultRootConfigurationService(
80         ConfigurationDocument configurationDocument) {
81
82         initialize(configurationDocument);
83         cache = ConfigurationCacheFactory.getInstance(this);
84     }
85
86     //////////////////////////////////////////////////////////////////
87
// Configuration Access
88
//////////////////////////////////////////////////////////////////
89

90     /** @see ConfigurationService#fetchConfiguration */
91     public Configuration fetchConfiguration(String JavaDoc configurationName) {
92         return cache.getConfiguration(configurationName);
93     }
94
95     /** @see ConfigurationService#fetchWritableConfiguration */
96     public Configuration fetchWritableConfiguration(String JavaDoc configurationName) {
97
98         Configuration returnConfig;
99
100         if (log.isTraceEnabled()) {
101             log.trace("Fetching configuration [" + configurationName + "]");
102         }
103
104         if (!configurationName.startsWith(String.valueOf(Node.DELIMITER))) {
105             throw new InvalidParameterException(
106                 this.getClass(),
107                 "[" + configurationName
108                     + "] doesnt start with ["
109                     + Node.DELIMITER
110                     + "]. All configuration names must start with ["
111                     + Node.DELIMITER
112                     + "]");
113         }
114
115         if (rootNode == null) {
116             throw new IllegalStateException JavaDoc(
117                 this.getClass(),
118                 "Configuration root node was null, this should not "
119                     + "happen. Check system logs");
120         }
121
122         try {
123             ConfigurationDocument configDocument =
124                 (ConfigurationDocument) traverseNodeTree(configurationName);
125             returnConfig = configDocument.readConfiguration();
126         } catch (ClassCastException JavaDoc cce) {
127             throw new InvalidParameterException(
128                 this.getClass(),
129                 "[" + configurationName + "] was found but "
130                     + "was not a ConfigurationDocument.", cce);
131
132         } catch (NodeNotFoundException nnfe) {
133             // Catch checked configuration exception and rethrow as runtime
134
throw new ConfigurationNotFoundException(
135                 this.getClass(),
136                 "The configuration [" + configurationName
137                     + "] was not found", nnfe);
138
139         } catch (NodeIOException nioe) {
140             // Catch checked configuration exception and rethrow as runtime
141
throw new ConfigurationAccessException(
142                 this.getClass(),
143                 "The configuration [" + configurationName
144                     + "] data could not be read "
145                     + "from the backing store.", nioe);
146
147         } catch (ConfigurationFormatException cfe) {
148             // Catch checked configuration exception and rethrow as runtime
149
throw new ConfigurationAccessException(
150                 this.getClass(),
151                 "The configuration [" + configurationName
152                     + "] data could not be read "
153                     + "from the backing store.", cfe);
154         }
155
156         return returnConfig;
157     }
158
159     /** @see ConfigurationService#storeConfiguration */
160     public void storeConfiguration(String JavaDoc configurationName,
161         Configuration config) throws ConfigurationStoreException {
162
163         if (!configurationName.startsWith(String.valueOf(Node.DELIMITER))) {
164             throw new InvalidParameterException(
165                 this.getClass(),
166                 "Configuration names must start with ["
167                     + Node.DELIMITER + "] supplied name was ["
168                     + configurationName + "]");
169         }
170
171         if (rootNode == null) {
172             throw new IllegalStateException JavaDoc(
173                 this.getClass(),
174                 "Configuration root node was null, this should not "
175                     + "happen. Check system logs");
176         }
177
178         try {
179             StringTokenizer JavaDoc tokenizer = new StringTokenizer JavaDoc(
180                                                 configurationName,
181                                                 String.valueOf(Node.DELIMITER));
182
183             recursiveStoreConfiguration(this.rootNode, config, tokenizer);
184
185         } catch (ConfigurationException ce) {
186             throw new ConfigurationStoreException(
187                 this.getClass(),
188                 "The configuration [" + configurationName
189                     + "] data could not be written "
190                     + "to the backing store.", ce);
191         }
192     }
193
194
195     /** @see ConfigurationService#createConfiguration */
196     public Configuration createConfiguration(Class JavaDoc configurationType) {
197         // TODO GH: Configurable
198
ConfigurationFormatService cfs =
199             new DefaultConfigurationFormatService();
200
201         return cfs.newConfiguration(configurationType);
202     }
203
204     //////////////////////////////////////////////////////////////////
205
// Node Access
206
//////////////////////////////////////////////////////////////////
207

208     /** @see ConfigurationService#fetchNode */
209     public Node fetchNode(String JavaDoc nodeName) throws NodeNotFoundException {
210         if (this.rootNode == null) {
211             throw new IllegalStateException JavaDoc(
212                 this.getClass(),
213                 "Configuration root node was null, this should not happen. "
214                     + "Check system logs");
215         }
216
217         return traverseNodeTree(nodeName);
218     }
219
220     /** @see ConfigurationService#addNodeListener */
221     public void addNodeListener(String JavaDoc nodeName, NodeEventListener listener)
222         throws NodeNotFoundException {
223
224         Node listenee = fetchNode(nodeName);
225         listenee.addNodeListener(listener);
226     }
227
228     /**
229      * @see ConfigurationService#nodeExists
230      */

231     public boolean nodeExists(String JavaDoc nodeName) {
232         if (this.rootNode == null) {
233             throw new IllegalStateException JavaDoc(
234                 this.getClass(),
235                 "Configuration root node was null, this should not "
236                     + "happen. Check system logs");
237         }
238
239         return traverseNodeTreeForNodeExists(nodeName);
240     }
241
242     //////////////////////////////////////////////////////////////////
243
// Configuration Service Configuration
244
//////////////////////////////////////////////////////////////////
245

246
247     /**
248      * Reads a LinkNodeConfiguration from the rootConfigurationDoc and
249      * uses the configured LinkNodeFactoryClass to create a new Node
250      * that is then set as the root node for the ConfigurationService.
251      *
252      * @see org.sape.carbon.core.config.node.link.LinkNode
253      * @see org.sape.carbon.core.config.node.link.LinkNodeConfiguration
254      *
255      * @param rootConfigurationDoc Contains data required to initialize this
256      * instance of <code>ConfigurationService</code> and its root node.
257      */

258     private void initialize(ConfigurationDocument rootConfigurationDoc) {
259
260         LinkNodeConfiguration rootConfiguration;
261         Class JavaDoc rootNodeFactoryClass = null;
262
263         try {
264             rootConfiguration = (LinkNodeConfiguration)
265                 rootConfigurationDoc.readConfiguration();
266
267             rootNodeFactoryClass =
268                 rootConfiguration.getLinkNodeFactoryClass();
269
270             if (rootNodeFactoryClass == null) {
271                 throw new ConfigurationInitializtionException(
272                     this.getClass(),
273                     "LinkNodeFactoryClass was not specified in "
274                         + "boot configuration document");
275             }
276
277             LinkNodeFactory rootNodeFactory = (LinkNodeFactory)
278                 rootNodeFactoryClass.newInstance();
279
280             this.rootNode = rootNodeFactory.getInstance(null, "",
281                 rootConfigurationDoc);
282
283         } catch (NodeIOException nioe) {
284             throw new ConfigurationInitializtionException(
285                 this.getClass(),
286                 "Could not read from root configuration document", nioe);
287
288         } catch (ConfigurationFormatException cfe) {
289             throw new ConfigurationInitializtionException(
290                 this.getClass(),
291                 "Could not read from root configuration document", cfe);
292
293         } catch (ClassCastException JavaDoc cce) {
294             if (rootNodeFactoryClass == null) {
295                 throw new ConfigurationInitializtionException(
296                     this.getClass(),
297                     "The boot configuration document did not contain a ["
298                         + LinkNodeConfiguration.class.getName() + "]", cce);
299             } else {
300                 throw new ConfigurationInitializtionException(
301                     this.getClass(),
302                     "LinkNodeFactoryClass ["
303                         + rootNodeFactoryClass.getName()
304                         + "] was not assignable from ["
305                         + LinkNodeFactory.class.getName() + "]", cce);
306             }
307
308         } catch (InstantiationException JavaDoc ie) {
309             throw new ConfigurationInitializtionException(
310                 this.getClass(),
311                 "Could not instantiate configured LinkNodeFactory ["
312                     + rootNodeFactoryClass.getName() + "]", ie);
313
314         } catch (IllegalAccessException JavaDoc iae) {
315             throw new ConfigurationInitializtionException(
316                 this.getClass(),
317                 "Could not instantiate configured LinkNodeFactory ["
318                     + rootNodeFactoryClass.getName() + "]", iae);
319
320         } catch (NodeCreationException nce) {
321             throw new ConfigurationInitializtionException(
322                 this.getClass(),
323                 "Could not create the root configuration node", nce);
324
325         } catch (Throwable JavaDoc t) {
326             throw new ConfigurationInitializtionException(this.getClass(), t);
327         }
328     }
329
330     //////////////////////////////////////////////////////////////////
331
// Node Traversal
332
//////////////////////////////////////////////////////////////////
333

334     /**
335      * Returns the <code>Node</code> found by recursively tokenizing the
336      * <code>configurationName</code> by '<code>/</code>' characters
337      * and scaling the node tree.
338      *
339      * @param configurationName The full logical name of the configuration
340      * to be fetched, such as <code>/nodeA/nodeB/configuration1</code>.
341      *
342      * @return The node corresponding to configurationName.
343      *
344      * @throws NodeNotFoundException when a node can not be fetched.
345      */

346     private Node traverseNodeTree(String JavaDoc configurationName)
347         throws NodeNotFoundException {
348
349         StringTokenizer JavaDoc tokenizer = new StringTokenizer JavaDoc(
350             configurationName, String.valueOf(Node.DELIMITER));
351
352         return recursiveNodeTraversal(this.rootNode, tokenizer);
353     }
354
355
356     /**
357      * Returns the <code>Node</code> found by recursively fetching the
358      * next node name from <code>tokenizer</code>
359      * and scaling the node tree.
360      *
361      * @param node the current parent node in the node traversal.
362      *
363      * @param tokenizer The StringTokenizer used to access the next
364      * node to be traversed.
365      *
366      * @return The node corresponding to the final token in
367      * <code>tokenizer</code>.
368      *
369      * @throws NodeNotFoundException when a node can not be fetched.
370      */

371     private Node recursiveNodeTraversal(Node node,
372         StringTokenizer JavaDoc tokenizer) throws NodeNotFoundException {
373
374         if (tokenizer.hasMoreTokens()) {
375             String JavaDoc nodeChild = tokenizer.nextToken();
376             return recursiveNodeTraversal(
377                 node.fetchChild(nodeChild), tokenizer);
378         } else {
379             return node;
380         }
381     }
382
383     /**
384      * Returns true if a node is found by recursively tokenizing the
385      * <code>configurationName</code> by '<code>/</code>' characters
386      * and scaling the node tree.
387      *
388      * @param configurationName The full logical name of the configuration
389      * to be fetched, such as <code>/nodeA/nodeB/configuration1</code>.
390      *
391      * @return true if the node exists or false otherwise
392      * @since carbon 1.1
393      */

394     private boolean traverseNodeTreeForNodeExists(String JavaDoc configurationName) {
395
396         StringTokenizer JavaDoc tokenizer = new StringTokenizer JavaDoc(
397             configurationName, String.valueOf(Node.DELIMITER));
398
399         return recursiveTraverseNodeTreeForNodeExists(this.rootNode, tokenizer);
400     }
401
402     /**
403      * Returns true if a node is found by recursively tokenizing the
404      * <code>configurationName</code> by '<code>/</code>' characters
405      * and scaling the node tree.
406      *
407      * @param node the current parent node in the node traversal.
408      * @param tokenizer The StringTokenizer used to access the next
409      * node to be traversed.
410      *
411      * @return true if the node exists or false otherwise
412      * @since carbon 1.1
413      */

414     private boolean recursiveTraverseNodeTreeForNodeExists(Node node,
415         StringTokenizer JavaDoc tokenizer) {
416
417         if (tokenizer.hasMoreTokens()) {
418             String JavaDoc nodeChild = tokenizer.nextToken();
419
420             if (!node.containsChild(nodeChild)) {
421                 return false;
422             }
423
424             try {
425                 return recursiveTraverseNodeTreeForNodeExists (
426                     node.fetchChild(nodeChild), tokenizer);
427             } catch (NodeNotFoundException nnfe) {
428                 // Shouldn't happen, but return false if it does
429
// happen due to some other thread deleting it
430
return false;
431             }
432         } else {
433             return true;
434         }
435     }
436
437     /**
438      * Traverses down the node adding the sub-nodes.
439      *
440      * @param node the node to add the children to
441      * @param config configuration to store
442      * @param tokenizer tokenizer of the full node name
443      *
444      * @throws NodeCreationException indicates an error creating the node
445      * @throws NodeIOException indicates an error reading the node
446      * @throws ConfigurationFormatException indicates an error in format
447      * of the configuration
448      */

449     private void recursiveStoreConfiguration(
450             Node node, Configuration config, StringTokenizer JavaDoc tokenizer)
451         throws
452             NodeCreationException, NodeIOException,
453             ConfigurationFormatException {
454
455         if (tokenizer.hasMoreTokens()) {
456             // there are more tokens, so we need to continue traversing
457
// down the node hierachy
458
try {
459                 String JavaDoc childNodeName = tokenizer.nextToken();
460                 Node childNode;
461                 if (!node.containsChild(childNodeName)) {
462                     // need to add the child so assume node is a Folder
463
Folder folder = (Folder) node;
464                     if (tokenizer.hasMoreTokens()) {
465                         // there are even more tokens, the childNode must be
466
// a Folder to contain the Node named by the next token
467
childNode = folder.addSubFolder(childNodeName);
468                     } else {
469                         // there are no more tokens, so the childNode must
470
// be a ConfigurationDocument named by the next token
471
childNode = folder.addConfigurationDocument(
472                             childNodeName, config);
473                     }
474
475                 } else {
476                     // throws NodeNotFoundException
477
childNode = node.fetchChild(childNodeName);
478                 }
479
480                 recursiveStoreConfiguration(childNode, config, tokenizer);
481
482             } catch (ClassCastException JavaDoc cce) {
483                 throw new InvalidParameterException(
484                     this.getClass(),
485                     "Configuration Node [" + node.getAbsoluteName() + "] "
486                         + "was not a Folder and could not contain children. "
487                         + "Remaining configuration name requested: ["
488                         + getRemainingTokens(tokenizer) + "]");
489
490             } catch (NodeNotFoundException nnfe) {
491                 throw new IllegalStateException JavaDoc(
492                     this.getClass(),
493                     "caught NodeNotFoundException when previous call "
494                         + "to containsChild returned true", nnfe);
495             }
496         } else {
497             try {
498                 ((ConfigurationDocument) node).writeConfiguration(config);
499             } catch (ClassCastException JavaDoc cce) {
500                 throw new InvalidParameterException(
501                     this.getClass(),
502                     "[" + node.getAbsoluteName() + "] was found but "
503                         + "was not a ConfigurationDocument.", cce);
504
505
506             }
507         }
508     }
509
510     /**
511      * Assembles the remaining tokens into a string, used for error messages.
512      *
513      * @param tokenizer the tokenizer to pull tokens from
514      * @return the remaining tokens delimited by <code>Node.DELIMITER</code>
515      */

516     private String JavaDoc getRemainingTokens(StringTokenizer JavaDoc tokenizer) {
517         StringBuffer JavaDoc serializedTokens = new StringBuffer JavaDoc();
518         while (tokenizer.hasMoreTokens()) {
519             serializedTokens.append(Node.DELIMITER);
520             serializedTokens.append(tokenizer.nextToken());
521         }
522         return serializedTokens.toString();
523
524     }
525
526 }
Popular Tags