View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.configuration;
19  
20  import java.io.File;
21  import java.io.InputStream;
22  import java.io.OutputStream;
23  import java.io.Reader;
24  import java.io.Writer;
25  import java.net.URL;
26  import java.util.Collection;
27  import java.util.Iterator;
28  import java.util.List;
29  
30  import org.apache.commons.configuration.event.ConfigurationErrorEvent;
31  import org.apache.commons.configuration.event.ConfigurationErrorListener;
32  import org.apache.commons.configuration.event.ConfigurationEvent;
33  import org.apache.commons.configuration.event.ConfigurationListener;
34  import org.apache.commons.configuration.reloading.Reloadable;
35  import org.apache.commons.configuration.reloading.ReloadingStrategy;
36  import org.apache.commons.configuration.tree.ConfigurationNode;
37  
38  /**
39   * <p>Base class for implementing file based hierarchical configurations.</p>
40   * <p>This class serves an analogous purpose as the
41   * {@link AbstractFileConfiguration} class for non hierarchical
42   * configurations. It behaves in exactly the same way, so please refer to the
43   * documentation of {@code AbstractFileConfiguration} for further details.</p>
44   *
45   * @since 1.2
46   *
47   * @author Emmanuel Bourg
48   * @version $Id: AbstractHierarchicalFileConfiguration.java 1206575 2011-11-26 20:07:52Z oheger $
49   */
50  public abstract class AbstractHierarchicalFileConfiguration
51  extends HierarchicalConfiguration
52  implements FileConfiguration, ConfigurationListener, ConfigurationErrorListener, FileSystemBased,
53          Reloadable
54  {
55      /** Stores the delegate used for implementing functionality related to the
56       * {@code FileConfiguration} interface.
57       */
58      private FileConfigurationDelegate delegate;
59  
60      /**
61       * Creates a new instance of {@code AbstractHierarchicalFileConfiguration}.
62       */
63      protected AbstractHierarchicalFileConfiguration()
64      {
65          initialize();
66      }
67  
68      /**
69       * Creates a new instance of
70       * {@code AbstractHierarchicalFileConfiguration} and copies the
71       * content of the specified configuration into this object.
72       *
73       * @param c the configuration to copy
74       * @since 1.4
75       */
76      protected AbstractHierarchicalFileConfiguration(HierarchicalConfiguration c)
77      {
78          super(c);
79          initialize();
80      }
81  
82      /**
83       * Creates and loads the configuration from the specified file.
84       *
85       * @param fileName The name of the plist file to load.
86       * @throws ConfigurationException Error while loading the file
87       */
88      public AbstractHierarchicalFileConfiguration(String fileName) throws ConfigurationException
89      {
90          this();
91          // store the file name
92          delegate.setFileName(fileName);
93  
94          // load the file
95          load();
96      }
97  
98      /**
99       * Creates and loads the configuration from the specified file.
100      *
101      * @param file The configuration file to load.
102      * @throws ConfigurationException Error while loading the file
103      */
104     public AbstractHierarchicalFileConfiguration(File file) throws ConfigurationException
105     {
106         this();
107         // set the file and update the url, the base path and the file name
108         setFile(file);
109 
110         // load the file
111         if (file.exists())
112         {
113             load();
114         }
115     }
116 
117     /**
118      * Creates and loads the configuration from the specified URL.
119      *
120      * @param url The location of the configuration file to load.
121      * @throws ConfigurationException Error while loading the file
122      */
123     public AbstractHierarchicalFileConfiguration(URL url) throws ConfigurationException
124     {
125         this();
126         // set the URL and update the base path and the file name
127         setURL(url);
128 
129         // load the file
130         load();
131     }
132 
133     /**
134      * Initializes this instance, mainly the internally used delegate object.
135      */
136     private void initialize()
137     {
138         delegate = createDelegate();
139         initDelegate(delegate);
140     }
141 
142     @Override
143     protected void addPropertyDirect(String key, Object obj)
144     {
145         synchronized (delegate.getReloadLock())
146         {
147             super.addPropertyDirect(key, obj);
148             delegate.possiblySave();
149         }
150     }
151 
152     @Override
153     public void clearProperty(String key)
154     {
155         synchronized (delegate.getReloadLock())
156         {
157             super.clearProperty(key);
158             delegate.possiblySave();
159         }
160     }
161 
162     @Override
163     public void clearTree(String key)
164     {
165         synchronized (delegate.getReloadLock())
166         {
167             super.clearTree(key);
168             delegate.possiblySave();
169         }
170     }
171 
172     @Override
173     public void setProperty(String key, Object value)
174     {
175         synchronized (delegate.getReloadLock())
176         {
177             super.setProperty(key, value);
178             delegate.possiblySave();
179         }
180     }
181 
182     public void load() throws ConfigurationException
183     {
184         delegate.load();
185     }
186 
187     public void load(String fileName) throws ConfigurationException
188     {
189         delegate.load(fileName);
190     }
191 
192     public void load(File file) throws ConfigurationException
193     {
194         delegate.load(file);
195     }
196 
197     public void load(URL url) throws ConfigurationException
198     {
199         delegate.load(url);
200     }
201 
202     public void load(InputStream in) throws ConfigurationException
203     {
204         delegate.load(in);
205     }
206 
207     public void load(InputStream in, String encoding) throws ConfigurationException
208     {
209         delegate.load(in, encoding);
210     }
211 
212     public void save() throws ConfigurationException
213     {
214         delegate.save();
215     }
216 
217     public void save(String fileName) throws ConfigurationException
218     {
219         delegate.save(fileName);
220     }
221 
222     public void save(File file) throws ConfigurationException
223     {
224         delegate.save(file);
225     }
226 
227     public void save(URL url) throws ConfigurationException
228     {
229         delegate.save(url);
230     }
231 
232     public void save(OutputStream out) throws ConfigurationException
233     {
234         delegate.save(out);
235     }
236 
237     public void save(OutputStream out, String encoding) throws ConfigurationException
238     {
239         delegate.save(out, encoding);
240     }
241 
242     public String getFileName()
243     {
244         return delegate.getFileName();
245     }
246 
247     public void setFileName(String fileName)
248     {
249         delegate.setFileName(fileName);
250     }
251 
252     public String getBasePath()
253     {
254         return delegate.getBasePath();
255     }
256 
257     public void setBasePath(String basePath)
258     {
259         delegate.setBasePath(basePath);
260     }
261 
262     public File getFile()
263     {
264         return delegate.getFile();
265     }
266 
267     public void setFile(File file)
268     {
269         delegate.setFile(file);
270     }
271 
272     public URL getURL()
273     {
274         return delegate.getURL();
275     }
276 
277     public void setURL(URL url)
278     {
279         delegate.setURL(url);
280     }
281 
282     public void setAutoSave(boolean autoSave)
283     {
284         delegate.setAutoSave(autoSave);
285     }
286 
287     public boolean isAutoSave()
288     {
289         return delegate.isAutoSave();
290     }
291 
292     public ReloadingStrategy getReloadingStrategy()
293     {
294         return delegate.getReloadingStrategy();
295     }
296 
297     public void setReloadingStrategy(ReloadingStrategy strategy)
298     {
299         delegate.setReloadingStrategy(strategy);
300     }
301 
302     public void reload()
303     {
304         reload(false);
305     }
306 
307     private boolean reload(boolean checkReload)
308     {
309         synchronized (delegate.getReloadLock())
310         {
311             setDetailEvents(false);
312             try
313             {
314                 return delegate.reload(checkReload);
315             }
316             finally
317             {
318                 setDetailEvents(true);
319             }
320         }
321     }
322 
323     /**
324      * Reloads the associated configuration file. This method first clears the
325      * content of this configuration, then the associated configuration file is
326      * loaded again. Updates on this configuration which have not yet been saved
327      * are lost. Calling this method is like invoking {@code reload()}
328      * without checking the reloading strategy.
329      *
330      * @throws ConfigurationException if an error occurs
331      * @since 1.7
332      */
333     public void refresh() throws ConfigurationException
334     {
335         delegate.refresh();
336     }
337 
338     public String getEncoding()
339     {
340         return delegate.getEncoding();
341     }
342 
343     public void setEncoding(String encoding)
344     {
345         delegate.setEncoding(encoding);
346     }
347 
348     @Override
349     public Object getReloadLock()
350     {
351         return delegate.getReloadLock();
352     }
353 
354     @Override
355     public boolean containsKey(String key)
356     {
357         reload();
358         synchronized (delegate.getReloadLock())
359         {
360             return super.containsKey(key);
361         }
362     }
363 
364     @Override
365     public Iterator<String> getKeys()
366     {
367         reload();
368         synchronized (delegate.getReloadLock())
369         {
370             return super.getKeys();
371         }
372     }
373 
374     @Override
375     public Iterator<String> getKeys(String prefix)
376     {
377         reload();
378         synchronized (delegate.getReloadLock())
379         {
380             return super.getKeys(prefix);
381         }
382     }
383 
384     @Override
385     public Object getProperty(String key)
386     {
387         if (reload(true))
388         {
389             // Avoid reloading again and getting the same error.
390             synchronized (delegate.getReloadLock())
391             {
392                 return super.getProperty(key);
393             }
394         }
395         return null;
396     }
397 
398     @Override
399     public boolean isEmpty()
400     {
401         reload();
402         synchronized (delegate.getReloadLock())
403         {
404             return super.isEmpty();
405         }
406     }
407 
408     /**
409      * Directly adds sub nodes to this configuration. This implementation checks
410      * whether auto save is necessary after executing the operation.
411      *
412      * @param key the key where the nodes are to be added
413      * @param nodes a collection with the nodes to be added
414      * @since 1.5
415      */
416     @Override
417     public void addNodes(String key, Collection<? extends ConfigurationNode> nodes)
418     {
419         synchronized (delegate.getReloadLock())
420         {
421             super.addNodes(key, nodes);
422             delegate.possiblySave();
423         }
424     }
425 
426     /**
427      * Fetches a list of nodes, which are selected by the specified key. This
428      * implementation will perform a reload if necessary.
429      *
430      * @param key the key
431      * @return a list with the selected nodes
432      */
433     @Override
434     protected List<ConfigurationNode> fetchNodeList(String key)
435     {
436         reload();
437         synchronized (delegate.getReloadLock())
438         {
439             return super.fetchNodeList(key);
440         }
441     }
442 
443     /**
444      * Reacts on changes of an associated subnode configuration. If the auto
445      * save mechanism is active, the configuration must be saved.
446      *
447      * @param event the event describing the change
448      * @since 1.5
449      */
450     @Override
451     protected void subnodeConfigurationChanged(ConfigurationEvent event)
452     {
453         delegate.possiblySave();
454         super.subnodeConfigurationChanged(event);
455     }
456 
457     /**
458      * Creates the file configuration delegate, i.e. the object that implements
459      * functionality required by the {@code FileConfiguration} interface.
460      * This base implementation will return an instance of the
461      * {@code FileConfigurationDelegate} class. Derived classes may
462      * override it to create a different delegate object.
463      *
464      * @return the file configuration delegate
465      */
466     protected FileConfigurationDelegate createDelegate()
467     {
468         return new FileConfigurationDelegate();
469     }
470 
471     /**
472      * Helper method for initializing the file configuration delegate.
473      *
474      * @param del the delegate
475      */
476     private void initDelegate(FileConfigurationDelegate del)
477     {
478         del.addConfigurationListener(this);
479         del.addErrorListener(this);
480         del.setLogger(getLogger());
481     }
482 
483     /**
484      * Reacts on configuration change events triggered by the delegate. These
485      * events are passed to the registered configuration listeners.
486      *
487      * @param event the triggered event
488      * @since 1.3
489      */
490     public void configurationChanged(ConfigurationEvent event)
491     {
492         // deliver reload events to registered listeners
493         setDetailEvents(true);
494         try
495         {
496             fireEvent(event.getType(), event.getPropertyName(), event
497                     .getPropertyValue(), event.isBeforeUpdate());
498         }
499         finally
500         {
501             setDetailEvents(false);
502         }
503     }
504 
505     public void configurationError(ConfigurationErrorEvent event)
506     {
507         fireError(event.getType(), event.getPropertyName(), event.getPropertyValue(),
508                 event.getCause());
509     }
510 
511     /**
512      * Returns the file configuration delegate.
513      *
514      * @return the delegate
515      */
516     protected FileConfigurationDelegate getDelegate()
517     {
518         return delegate;
519     }
520 
521     /**
522      * Allows to set the file configuration delegate.
523      * @param delegate the new delegate
524      */
525     protected void setDelegate(FileConfigurationDelegate delegate)
526     {
527         this.delegate = delegate;
528     }
529 
530     /**
531      * Set the FileSystem to be used for this Configuration.
532      * @param fileSystem The FileSystem to use.
533      */
534     public void setFileSystem(FileSystem fileSystem)
535     {
536         delegate.setFileSystem(fileSystem);
537     }
538 
539     /**
540      * Reset the FileSystem to the default;
541      */
542     public void resetFileSystem()
543     {
544         delegate.resetFileSystem();
545     }
546 
547     /**
548      * Retrieve the FileSystem being used.
549      * @return The FileSystem.
550      */
551     public FileSystem getFileSystem()
552     {
553         return delegate.getFileSystem();
554     }
555 
556     /**
557      * A special implementation of the {@code FileConfiguration} interface that is
558      * used internally to implement the {@code FileConfiguration} methods
559      * for hierarchical configurations.
560      */
561     protected class FileConfigurationDelegate extends AbstractFileConfiguration
562     {
563         public void load(Reader in) throws ConfigurationException
564         {
565             AbstractHierarchicalFileConfiguration.this.load(in);
566         }
567 
568         public void save(Writer out) throws ConfigurationException
569         {
570             AbstractHierarchicalFileConfiguration.this.save(out);
571         }
572 
573         @Override
574         public void clear()
575         {
576             AbstractHierarchicalFileConfiguration.this.clear();
577         }
578     }
579 }