KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > sape > carbon > core > config > node > AbstractConfigurationDocument


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.node;
19
20 import java.io.InputStream JavaDoc;
21 import java.io.OutputStream JavaDoc;
22 import java.util.Iterator JavaDoc;
23 import java.util.Set JavaDoc;
24
25 import org.sape.carbon.core.config.Config;
26 import org.sape.carbon.core.config.Configuration;
27 import org.sape.carbon.core.config.ConfigurationException;
28 import org.sape.carbon.core.config.InvalidConfigurationException;
29 import org.sape.carbon.core.config.format.ConfigurationFormatException;
30 import org.sape.carbon.core.config.format.ConfigurationFormatService;
31 import org.sape.carbon.core.exception.ExceptionUtility;
32 import org.sape.carbon.core.exception.InvalidParameterException;
33
34 import org.apache.commons.logging.Log;
35 import org.apache.commons.logging.LogFactory;
36
37 /**
38  * This implementation of <code>ConfigurationDocument</code> uses a
39  * <code>ConfigurationFormatService</code> to read and write
40  * <code>Configuration</code>s from and to the backing data store and
41  * caches the results for future use.
42  * This implementation interfaces with the backing data store via sub-classes
43  * implementing the methods <code>openInputStream</code> and
44  * <code>openOutputStream</code>. This implementation caches the
45  * <code>Configuration</code> object upon the first call to readConfiguration.
46  * The cached version is cleared when writeConfiguration is called.
47  *
48  * Copyright 2002 Sapient
49  * @since carbon 1.0
50  * @author Douglas Voet, February 2002
51  * @version $Revision: 1.33 $($Author: dvoet $ / $Date: 2003/10/30 19:13:04 $)
52  */

53 public abstract class AbstractConfigurationDocument
54     extends AbstractNode
55     implements ConfigurationDocument {
56
57     /**
58      * Provides a handle to Apache-commons logger
59      */

60     private Log log =
61         LogFactory.getLog(this.getClass());
62
63     /** Cached copy of the <code>Configuration</code> */
64     protected Configuration configuration = null;
65
66     /** The ConfigurationFormatService used by this class. */
67     private final ConfigurationFormatService formatter;
68
69     /** Document factory for nested configurations. */
70     private final NodeFactory nestedNodeFactory =
71         new NestedConfigurationDocumentFactory();
72
73     /**
74      * @param parent
75      * @param name
76      * @param readOrAlterNodeLock
77      */

78     public AbstractConfigurationDocument(
79         Node parent,
80         String JavaDoc name,
81         Object JavaDoc readOrAlterNodeLock,
82         Object JavaDoc addOrLoadChildLock,
83         ConfigurationFormatService formatter) {
84
85         super(parent, name, readOrAlterNodeLock, addOrLoadChildLock);
86
87         // parameter check
88
if (formatter == null) {
89             throw new InvalidParameterException(
90                 this.getClass(),
91                 "formatter cannot be null");
92         }
93         this.formatter = formatter;
94     }
95
96     /**
97      * Constructor for AbstractConfigurationDocument.
98      * @param parent the node's parent
99      * @param name the node's name
100      * @param formatter the ConfigurationFormatService object used
101      * to read and write Configuration to the backing data store
102      *
103      * @throws NullPointerException if formatter is null
104      */

105     public AbstractConfigurationDocument(
106         Node parent,
107         String JavaDoc name,
108         ConfigurationFormatService formatter) {
109
110         super(parent, name);
111
112         // parameter check
113
if (formatter == null) {
114             throw new InvalidParameterException(
115                 this.getClass(),
116                 "formatter cannot be null");
117         }
118         this.formatter = formatter;
119     }
120
121     /**
122      * This implementation uses the <code>ConfigurationFormatService</code>
123      * to read from the backing data store. It calls the abstract method
124      * <code>openInputStream</code> to open an <code>InputStream</code>
125      * to read the <code>Configuration</code> from then closes it before
126      * it exits.
127      * <p>
128      * This method is synchronized to prevent multiple readers from loading
129      * the <code>Configuration</code> at once and to make sure that
130      * no one is writing a new <code>Configuration</code> while someone else
131      * is reading.
132      *
133      * @return the <code>Configuration</code> object within this
134      * <code>ConfigurationDocument</code>
135      * @throws NodeIOException indicates a generic exception writing the
136      * configuration
137      * @throws ConfigurationFormatException indicates an error formating
138      * the config document
139      */

140     public Configuration readConfiguration()
141         throws NodeIOException, ConfigurationFormatException {
142             
143         synchronized (getReadOrAlterNodeLock()) {
144     
145             if (isRemoved()) {
146                 throw new NodeRemovedException(
147                     this.getClass(),
148                     this);
149             }
150     
151             if (this.configuration == null) {
152                 InputStream JavaDoc in = null;
153                 try {
154     
155                     in = openInputStream();
156                     this.configuration =
157                         this.formatter.readConfigurationStream(
158                             getAbsoluteName(),
159                             in);
160     
161                 } catch (Exception JavaDoc e) {
162                     throw new NodeIOException(
163                         this.getClass(),
164                         this.getAbsoluteName(),
165                         e);
166                 } finally {
167                     if (in != null) {
168                         // close the stream.
169
try {
170                             closeInputStream(in);
171                         } catch (Exception JavaDoc e) {
172                             // log and eat the exception
173
// just wanted to make sure the stream is closed
174
if (log.isWarnEnabled()) {
175                                 log.warn("Caught exception while "
176                                     + "closing InputStream: "
177                                     + ExceptionUtility.printStackTracesToString(e));
178                             }
179                         }
180                     }
181                 }
182             }
183             return (Configuration) this.configuration.clone();
184         }
185
186     }
187
188     /**
189      * This implementation uses the <code>ConfigurationFormatService</code>
190      * to write to the backing data store. It calls the abstract method
191      * <code>openOutpuStream</code> to open an <code>OutputStream</code>
192      * to write the <code>Configuration</code> to then closes it before
193      * it exits.
194      * <p>
195      * This method is synchronized internally to prevent multiple writes from
196      * saving <code>Configuration</code>s concurrently and to make sure that
197      * no one is writing a new <code>Configuration</code> while someone else
198      * is reading.
199      *
200      * @param config the <code>Configuration</code> to write to the backing
201      * data store
202      * @throws NodeIOException indicates a generic exception writing the
203      * configuration
204      * @throws ConfigurationFormatException indicates an error formating
205      * the config document
206      */

207     public void writeConfiguration(Configuration config)
208         throws NodeIOException, ConfigurationFormatException {
209
210         synchronized (getReadOrAlterNodeLock()) {
211             if (isRemoved()) {
212                 throw new NodeRemovedException(
213                     this.getClass(),
214                     this);
215             }
216
217             OutputStream JavaDoc out = null;
218             try {
219                 //writeChildReferences does not work yet
220
//writeChildReferences(config);
221
out = openOutputStream();
222                 formatter.writeConfigurationStream(config, out);
223                 out.flush();
224             } catch (Exception JavaDoc e) {
225                 throw new NodeIOException(
226                     this.getClass(),
227                     this.getAbsoluteName(),
228                     e);
229             } finally {
230                 if (out != null) {
231                     // close the stream.
232
try {
233                         closeOutputStream(out);
234                     } catch (Exception JavaDoc e) {
235                         // log and eat the exception
236
// just wanted to make sure the stream is closed
237
if (log.isWarnEnabled()) {
238                             log.warn("Caught exception while "
239                                 + "closing OutputStream: "
240                                 + ExceptionUtility.printStackTracesToString(
241                                     e));
242                         }
243                     }
244                 }
245             }
246
247             // set this.configuration to null so next call to readConfiguration
248
// will reload the configuration
249
this.configuration = null;
250         }
251
252         issueNodeChangedEvent();
253         notifyNestedDocuments();
254     }
255
256     /**
257      * @see Node#remove()
258      *
259      * synchronized to ensure no one is fetching or adding children while
260      * this method is removing them
261      */

262     public int remove() throws NodeRemovalException {
263         int removedNodes = 0;
264
265         if (!isRemoved()) {
266             synchronized (getReadOrAlterNodeLock()) {
267                 // remove all the children
268
// note that destroyBackingData actually removes the children so
269
// there is no need to call remove on each child
270
this.childNodes.clear();
271
272                 // remove this node
273
if (backingDataExists()) {
274                     destroyBackingData();
275                 }
276                 setRemoved();
277                 removedNodes++;
278             }
279
280             issueNodeRemovedEvent();
281         }
282
283         return removedNodes;
284     }
285
286     /**
287      * @inherit
288      * @see AbstractNode#addNestedConfigurationDocument
289      */

290     public ConfigurationDocument addNestedConfigurationDocument(
291         String JavaDoc name,
292         Configuration config)
293         throws NodeCreationException {
294
295         if (isRemoved()) {
296             throw new NodeRemovedException(
297                 this.getClass(),
298                 this);
299         }
300
301         try {
302
303             // this returns an instance of NestedConfigurationDocument
304
// even though config might be a reference
305
ConfigurationDocument newDocument =
306                 (ConfigurationDocument) nestedNodeFactory.getInstance(
307                     this,
308                     name);
309                     
310             synchronized (getReadOrAlterNodeLock()) {
311                 if (containsChild(name)) {
312                     throw new NodeCreationException(
313                         this.getClass(),
314                         this,
315                         name,
316                         "Node already exists");
317                 }
318                 
319                 // this writes the config correctly regardless of whether or not
320
// config should be a reference or embedded.
321
newDocument.writeConfiguration(config);
322             }
323             
324             // at this point, we don't know if newDocument is valid. it would
325
// be invalid if config was written as a reference and not embedded.
326
// fetch child should now return the valid object (and cache it)
327
return (ConfigurationDocument) fetchChild(name);
328
329         } catch (NodeNotFoundException nnfe) {
330             throw new NodeCreationException(
331                 this.getClass(),
332                 this,
333                 name,
334                 "Exception writing new configuration",
335                 nnfe);
336
337         } catch (NodeIOException nioe) {
338             throw new NodeCreationException(
339                 this.getClass(),
340                 this,
341                 name,
342                 "Exception writing new configuration",
343                 nioe);
344
345         } catch (ConfigurationFormatException cfe) {
346             throw new NodeCreationException(
347                 this.getClass(),
348                 this,
349                 name,
350                 "Exception writing new configuration",
351                 cfe);
352         }
353     }
354
355     /**
356      * @see org.sape.carbon.core.config.node.ConfigurationDocument#getFormatService()
357      */

358     public ConfigurationFormatService getFormatService() {
359         return this.formatter;
360     }
361
362     /**
363      * @see org.sape.carbon.core.config.node.Node#refresh()
364      */

365     public void refresh() {
366         if (!isRemoved()) {
367             if (backingDataExists()) {
368                 synchronized (getReadOrAlterNodeLock()) {
369                     // set configuration copy to null to force a read
370
// this should refresh nested configurations
371
// nested configurations that are references should not be
372
// refreshed because it could cause a circular reference
373
this.configuration = null;
374                 }
375                 removeRemovedChildren();
376                 // node may have changed, issue an event just in case
377
issueNodeChangedEvent();
378
379             } else {
380                 synchronized (getReadOrAlterNodeLock()) {
381                     // backing data is not there anymore!
382
setRemoved();
383                 }
384                 // node has be removed.
385
issueNodeRemovedEvent();
386             }
387         }
388     }
389
390     /**
391      * This method notifies all nested configuration documents that the
392      * document has changed and their listeners should be notified.
393      *
394      * @since carbon 1.1
395      */

396     protected void notifyNestedDocuments() {
397         Iterator JavaDoc childIterator = super.childNodes.values().iterator();
398
399         while (childIterator.hasNext()) {
400             try {
401                 NestedConfigurationDocument child =
402                     (NestedConfigurationDocument) childIterator.next();
403
404                 child.onChange();
405             } catch (ClassCastException JavaDoc cce) {
406                 // we only want to notify nested documents
407
// the current child is not a NestedConfigurationDocument
408
// so we don't do anything, eat the exception
409
}
410         }
411     }
412
413     /**
414      * This method should be overridden by classes implementing
415      * <code>ConfigurationDocument</code>s for specific data stores.
416      * The returned <code>InputStream</code> should be a stream of data
417      * that can be read by the <code>ConfigurationFormatService</code> and
418      * converted to a <code>Configuration</code> object.
419      * <p>
420      * This method is called by readConfiguration.
421      *
422      * @return InputStream the <code>InputStream</code> from which to read
423      * the <code>Configuration</code> object within this
424      * <code>ConfigurationDocument</code>
425      *
426      * @throws Exception if an exception occurs openning the stream
427      */

428     protected abstract InputStream JavaDoc openInputStream() throws Exception JavaDoc;
429
430     /**
431      * Closes the input stream.
432      *
433      * @param in the stream to open
434      * @throws Exception indicates an exception occured closing the stream
435      */

436     protected void closeInputStream(InputStream JavaDoc in) throws Exception JavaDoc {
437         in.close();
438     }
439
440     /**
441      * This method should be overridden by classes implementing
442      * <code>ConfigurationDocument</code>s for specific data stores.
443      * The returned <code>OutputStream</code> should be a stream of data
444      * that can be written to by the <code>ConfigurationFormatService</code>.
445      * <p>
446      * This method is called by writeConfiguration.
447      *
448      * @return OutputStream the <code>OutputStream</code> to which to write
449      * the <code>Configuration</code> object within this
450      * <code>ConfigurationDocument</code>
451      *
452      * @throws Exception if an exception occurs openning the stream
453      */

454     protected abstract OutputStream JavaDoc openOutputStream() throws Exception JavaDoc;
455
456
457     /**
458      * Closes the output stream.
459      *
460      * @param out the stream to close
461      * @throws Exception indicates an error closing the stream
462      */

463     protected void closeOutputStream(OutputStream JavaDoc out) throws Exception JavaDoc {
464         out.close();
465     }
466
467     /**
468      * Retreives the names of all child configurations.
469      *
470      * @return names of all child configurations
471      */

472     protected Set JavaDoc getAllChildNames() {
473         try {
474
475             return this.formatter.getChildConfigurationNames(
476                 readConfiguration());
477
478         } catch (NodeIOException nioe) {
479             throw new InvalidConfigurationException(
480                 this.getClass(),
481                 getAbsoluteName(),
482                 getName(),
483                 "Could not read child names",
484                 nioe);
485
486         } catch (ConfigurationFormatException cfe) {
487             throw new InvalidConfigurationException(
488                 this.getClass(),
489                 getAbsoluteName(),
490                 getName(),
491                 "Could not read child names",
492                 cfe);
493         }
494     }
495
496     /**
497      * @see org.sape.carbon.core.config.node.AbstractNode#loadChild(String)
498      */

499     protected Node loadChild(String JavaDoc nodeName) throws NodeNotFoundException {
500         try {
501             ConfigurationDocument nestedDoc =
502                 (ConfigurationDocument) nestedNodeFactory.getInstance(
503                     this,
504                     nodeName);
505             // need to refresh to make sure the node checks if its backing
506
// data exists
507
nestedDoc.refresh();
508             if (nestedDoc.isRemoved()) {
509                 throw new NodeNotFoundException(
510                     this.getClass(),
511                     nodeName);
512
513             } else {
514                 return nestedDoc;
515             }
516         } catch (NodeCreationException nce) {
517             throw new NodeNotFoundException(
518                 this.getClass(),
519                 nodeName,
520                 nce);
521         }
522     }
523
524     /**
525      * Writes out references to child configuration documents
526      * inside a config.
527      *
528      * @param config config to persist out child references
529      * @throws ConfigurationFormatException indicates an error
530      * writing out the child reference
531      */

532     protected void writeChildReferences(Configuration config)
533         throws ConfigurationFormatException {
534
535         try {
536             Set JavaDoc childNames = this.formatter.getChildConfigurationNames(config);
537             Iterator JavaDoc childIterator = childNames.iterator();
538
539             while (childIterator.hasNext()) {
540                 String JavaDoc childName = (String JavaDoc) childIterator.next();
541                 Configuration childConfig =
542                     this.formatter.getChildConfiguration(config, childName);
543                 String JavaDoc childAbsoluteName = childConfig.getConfigurationName();
544                 if (!childAbsoluteName.startsWith("null")
545                     && !childAbsoluteName.startsWith(
546                         getAbsoluteName() + Node.DELIMITER)) {
547                     Config.getInstance().storeConfiguration(
548                         childAbsoluteName,
549                         childConfig);
550                 }
551             }
552         } catch (ConfigurationException ce) {
553             throw new ConfigurationFormatException(
554                 this.getClass(),
555                 "Could not write child references",
556                 ce);
557         }
558     }
559
560     /**
561      * @see org.sape.carbon.core.config.node.ConfigurationDocument#getNestedNodeFactory()
562      */

563     public NodeFactory getNestedNodeFactory() {
564         return this.nestedNodeFactory;
565     }
566
567 }
568
Popular Tags