1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.configuration;
19
20 import java.io.File;
21 import java.io.FileOutputStream;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.InputStreamReader;
25 import java.io.OutputStream;
26 import java.io.OutputStreamWriter;
27 import java.io.Reader;
28 import java.io.UnsupportedEncodingException;
29 import java.io.Writer;
30 import java.net.HttpURLConnection;
31 import java.net.MalformedURLException;
32 import java.net.URL;
33 import java.net.URLConnection;
34 import java.util.Iterator;
35 import java.util.LinkedList;
36 import java.util.List;
37
38 import org.apache.commons.configuration.reloading.InvariantReloadingStrategy;
39 import org.apache.commons.configuration.reloading.ReloadingStrategy;
40 import org.apache.commons.lang.StringUtils;
41 import org.apache.commons.logging.LogFactory;
42
43 /***
44 * <p>Partial implementation of the <code>FileConfiguration</code> interface.
45 * Developers of file based configuration may want to extend this class,
46 * the two methods left to implement are <code>{@link FileConfiguration#load(Reader)}</code>
47 * and <code>{@link FileConfiguration#save(Writer)}</code>.</p>
48 * <p>This base class already implements a couple of ways to specify the location
49 * of the file this configuration is based on. The following possibilities
50 * exist:
51 * <ul><li>URLs: With the method <code>setURL()</code> a full URL to the
52 * configuration source can be specified. This is the most flexible way. Note
53 * that the <code>save()</code> methods support only <em>file:</em> URLs.</li>
54 * <li>Files: The <code>setFile()</code> method allows to specify the
55 * configuration source as a file. This can be either a relative or an
56 * absolute file. In the former case the file is resolved based on the current
57 * directory.</li>
58 * <li>As file paths in string form: With the <code>setPath()</code> method a
59 * full path to a configuration file can be provided as a string.</li>
60 * <li>Separated as base path and file name: This is the native form in which
61 * the location is stored. The base path is a string defining either a local
62 * directory or a URL. It can be set using the <code>setBasePath()</code>
63 * method. The file name, non surprisingly, defines the name of the configuration
64 * file.</li></ul></p>
65 * <p>Note that the <code>load()</code> methods do not wipe out the configuration's
66 * content before the new configuration file is loaded. Thus it is very easy to
67 * construct a union configuration by simply loading multiple configuration
68 * files, e.g.</p>
69 * <p><pre>
70 * config.load(configFile1);
71 * config.load(configFile2);
72 * </pre></p>
73 * <p>After executing this code fragment, the resulting configuration will
74 * contain both the properties of configFile1 and configFile2. On the other
75 * hand, if the current configuration file is to be reloaded, <code>clear()</code>
76 * should be called first. Otherwise the properties are doubled. This behavior
77 * is analogous to the behavior of the <code>load(InputStream)</code> method
78 * in <code>java.util.Properties</code>.</p>
79 *
80 * @author Emmanuel Bourg
81 * @version $Revision: 712401 $, $Date: 2008-11-08 16:29:56 +0100 (Sa, 08 Nov 2008) $
82 * @since 1.0-rc2
83 */
84 public abstract class AbstractFileConfiguration extends BaseConfiguration implements FileConfiguration
85 {
86 /*** Constant for the configuration reload event.*/
87 public static final int EVENT_RELOAD = 20;
88
89 /*** Stores the file name.*/
90 protected String fileName;
91
92 /*** Stores the base path.*/
93 protected String basePath;
94
95 /*** The auto save flag.*/
96 protected boolean autoSave;
97
98 /*** Holds a reference to the reloading strategy.*/
99 protected ReloadingStrategy strategy;
100
101 /*** A lock object for protecting reload operations.*/
102 private Object reloadLock = new Object();
103
104 /*** Stores the encoding of the configuration file.*/
105 private String encoding;
106
107 /*** Stores the URL from which the configuration file was loaded.*/
108 private URL sourceURL;
109
110 /*** A counter that prohibits reloading.*/
111 private int noReload;
112
113 /***
114 * Default constructor
115 *
116 * @since 1.1
117 */
118 public AbstractFileConfiguration()
119 {
120 initReloadingStrategy();
121 setLogger(LogFactory.getLog(getClass()));
122 addErrorLogListener();
123 }
124
125 /***
126 * Creates and loads the configuration from the specified file. The passed
127 * in string must be a valid file name, either absolute or relativ.
128 *
129 * @param fileName The name of the file to load.
130 *
131 * @throws ConfigurationException Error while loading the file
132 * @since 1.1
133 */
134 public AbstractFileConfiguration(String fileName) throws ConfigurationException
135 {
136 this();
137
138
139 setFileName(fileName);
140
141
142 load();
143 }
144
145 /***
146 * Creates and loads the configuration from the specified file.
147 *
148 * @param file The file to load.
149 * @throws ConfigurationException Error while loading the file
150 * @since 1.1
151 */
152 public AbstractFileConfiguration(File file) throws ConfigurationException
153 {
154 this();
155
156
157 setFile(file);
158
159
160 if (file.exists())
161 {
162 load();
163 }
164 }
165
166 /***
167 * Creates and loads the configuration from the specified URL.
168 *
169 * @param url The location of the file to load.
170 * @throws ConfigurationException Error while loading the file
171 * @since 1.1
172 */
173 public AbstractFileConfiguration(URL url) throws ConfigurationException
174 {
175 this();
176
177
178 setURL(url);
179
180
181 load();
182 }
183
184 /***
185 * Load the configuration from the underlying location.
186 *
187 * @throws ConfigurationException if loading of the configuration fails
188 */
189 public void load() throws ConfigurationException
190 {
191 if (sourceURL != null)
192 {
193 load(sourceURL);
194 }
195 else
196 {
197 load(getFileName());
198 }
199 }
200
201 /***
202 * Locate the specified file and load the configuration. This does not
203 * change the source of the configuration (i.e. the internally maintained file name).
204 * Use one of the setter methods for this purpose.
205 *
206 * @param fileName the name of the file to be loaded
207 * @throws ConfigurationException if an error occurs
208 */
209 public void load(String fileName) throws ConfigurationException
210 {
211 try
212 {
213 URL url = ConfigurationUtils.locate(basePath, fileName);
214
215 if (url == null)
216 {
217 throw new ConfigurationException("Cannot locate configuration source " + fileName);
218 }
219 load(url);
220 }
221 catch (ConfigurationException e)
222 {
223 throw e;
224 }
225 catch (Exception e)
226 {
227 throw new ConfigurationException("Unable to load the configuration file " + fileName, e);
228 }
229 }
230
231 /***
232 * Load the configuration from the specified file. This does not change
233 * the source of the configuration (i.e. the internally maintained file
234 * name). Use one of the setter methods for this purpose.
235 *
236 * @param file the file to load
237 * @throws ConfigurationException if an error occurs
238 */
239 public void load(File file) throws ConfigurationException
240 {
241 try
242 {
243 load(ConfigurationUtils.toURL(file));
244 }
245 catch (ConfigurationException e)
246 {
247 throw e;
248 }
249 catch (Exception e)
250 {
251 throw new ConfigurationException("Unable to load the configuration file " + file, e);
252 }
253 }
254
255 /***
256 * Load the configuration from the specified URL. This does not change the
257 * source of the configuration (i.e. the internally maintained file name).
258 * Use on of the setter methods for this purpose.
259 *
260 * @param url the URL of the file to be loaded
261 * @throws ConfigurationException if an error occurs
262 */
263 public void load(URL url) throws ConfigurationException
264 {
265 if (sourceURL == null)
266 {
267 if (StringUtils.isEmpty(getBasePath()))
268 {
269
270 setBasePath(url.toString());
271 }
272 sourceURL = url;
273 }
274
275
276 File file = ConfigurationUtils.fileFromURL(url);
277 if (file != null && file.isDirectory())
278 {
279 throw new ConfigurationException("Cannot load a configuration from a directory");
280 }
281
282 InputStream in = null;
283
284 try
285 {
286 in = url.openStream();
287 load(in);
288 }
289 catch (ConfigurationException e)
290 {
291 throw e;
292 }
293 catch (Exception e)
294 {
295 throw new ConfigurationException("Unable to load the configuration from the URL " + url, e);
296 }
297 finally
298 {
299
300 try
301 {
302 if (in != null)
303 {
304 in.close();
305 }
306 }
307 catch (IOException e)
308 {
309 getLogger().warn("Could not close input stream", e);
310 }
311 }
312 }
313
314 /***
315 * Load the configuration from the specified stream, using the encoding
316 * returned by {@link #getEncoding()}.
317 *
318 * @param in the input stream
319 *
320 * @throws ConfigurationException if an error occurs during the load operation
321 */
322 public void load(InputStream in) throws ConfigurationException
323 {
324 load(in, getEncoding());
325 }
326
327 /***
328 * Load the configuration from the specified stream, using the specified
329 * encoding. If the encoding is null the default encoding is used.
330 *
331 * @param in the input stream
332 * @param encoding the encoding used. <code>null</code> to use the default encoding
333 *
334 * @throws ConfigurationException if an error occurs during the load operation
335 */
336 public void load(InputStream in, String encoding) throws ConfigurationException
337 {
338 Reader reader = null;
339
340 if (encoding != null)
341 {
342 try
343 {
344 reader = new InputStreamReader(in, encoding);
345 }
346 catch (UnsupportedEncodingException e)
347 {
348 throw new ConfigurationException(
349 "The requested encoding is not supported, try the default encoding.", e);
350 }
351 }
352
353 if (reader == null)
354 {
355 reader = new InputStreamReader(in);
356 }
357
358 load(reader);
359 }
360
361 /***
362 * Save the configuration. Before this method can be called a valid file
363 * name must have been set.
364 *
365 * @throws ConfigurationException if an error occurs or no file name has
366 * been set yet
367 */
368 public void save() throws ConfigurationException
369 {
370 if (getFileName() == null)
371 {
372 throw new ConfigurationException("No file name has been set!");
373 }
374
375 if (sourceURL != null)
376 {
377 save(sourceURL);
378 }
379 else
380 {
381 save(fileName);
382 }
383 strategy.init();
384 }
385
386 /***
387 * Save the configuration to the specified file. This doesn't change the
388 * source of the configuration, use setFileName() if you need it.
389 *
390 * @param fileName the file name
391 *
392 * @throws ConfigurationException if an error occurs during the save operation
393 */
394 public void save(String fileName) throws ConfigurationException
395 {
396 try
397 {
398 File file = ConfigurationUtils.getFile(basePath, fileName);
399 if (file == null)
400 {
401 throw new ConfigurationException("Invalid file name for save: " + fileName);
402 }
403 save(file);
404 }
405 catch (ConfigurationException e)
406 {
407 throw e;
408 }
409 catch (Exception e)
410 {
411 throw new ConfigurationException("Unable to save the configuration to the file " + fileName, e);
412 }
413 }
414
415 /***
416 * Save the configuration to the specified URL.
417 * This doesn't change the source of the configuration, use setURL()
418 * if you need it.
419 *
420 * @param url the URL
421 *
422 * @throws ConfigurationException if an error occurs during the save operation
423 */
424 public void save(URL url) throws ConfigurationException
425 {
426
427
428 File file = ConfigurationUtils.fileFromURL(url);
429 if (file != null)
430 {
431 save(file);
432 }
433 else
434 {
435
436 OutputStream out = null;
437 try
438 {
439 URLConnection connection = url.openConnection();
440 connection.setDoOutput(true);
441
442
443 if (connection instanceof HttpURLConnection)
444 {
445 HttpURLConnection conn = (HttpURLConnection) connection;
446 conn.setRequestMethod("PUT");
447 }
448
449 out = connection.getOutputStream();
450 save(out);
451
452
453 if (connection instanceof HttpURLConnection)
454 {
455 HttpURLConnection conn = (HttpURLConnection) connection;
456 if (conn.getResponseCode() >= HttpURLConnection.HTTP_BAD_REQUEST)
457 {
458 throw new IOException("HTTP Error " + conn.getResponseCode() + " " + conn.getResponseMessage());
459 }
460 }
461 }
462 catch (IOException e)
463 {
464 throw new ConfigurationException("Could not save to URL " + url, e);
465 }
466 finally
467 {
468 closeSilent(out);
469 }
470 }
471 }
472
473 /***
474 * Save the configuration to the specified file. The file is created
475 * automatically if it doesn't exist. This doesn't change the source
476 * of the configuration, use {@link #setFile} if you need it.
477 *
478 * @param file the target file
479 *
480 * @throws ConfigurationException if an error occurs during the save operation
481 */
482 public void save(File file) throws ConfigurationException
483 {
484 OutputStream out = null;
485
486 try
487 {
488
489 createPath(file);
490 out = new FileOutputStream(file);
491 save(out);
492 }
493 catch (IOException e)
494 {
495 throw new ConfigurationException("Unable to save the configuration to the file " + file, e);
496 }
497 finally
498 {
499 closeSilent(out);
500 }
501 }
502
503 /***
504 * Save the configuration to the specified stream, using the encoding
505 * returned by {@link #getEncoding()}.
506 *
507 * @param out the output stream
508 *
509 * @throws ConfigurationException if an error occurs during the save operation
510 */
511 public void save(OutputStream out) throws ConfigurationException
512 {
513 save(out, getEncoding());
514 }
515
516 /***
517 * Save the configuration to the specified stream, using the specified
518 * encoding. If the encoding is null the default encoding is used.
519 *
520 * @param out the output stream
521 * @param encoding the encoding to use
522 * @throws ConfigurationException if an error occurs during the save operation
523 */
524 public void save(OutputStream out, String encoding) throws ConfigurationException
525 {
526 Writer writer = null;
527
528 if (encoding != null)
529 {
530 try
531 {
532 writer = new OutputStreamWriter(out, encoding);
533 }
534 catch (UnsupportedEncodingException e)
535 {
536 throw new ConfigurationException(
537 "The requested encoding is not supported, try the default encoding.", e);
538 }
539 }
540
541 if (writer == null)
542 {
543 writer = new OutputStreamWriter(out);
544 }
545
546 save(writer);
547 }
548
549 /***
550 * Return the name of the file.
551 *
552 * @return the file name
553 */
554 public String getFileName()
555 {
556 return fileName;
557 }
558
559 /***
560 * Set the name of the file. The passed in file name can contain a
561 * relative path.
562 * It must be used when referring files with relative paths from classpath.
563 * Use <code>{@link AbstractFileConfiguration#setPath(String)
564 * setPath()}</code> to set a full qualified file name.
565 *
566 * @param fileName the name of the file
567 */
568 public void setFileName(String fileName)
569 {
570 sourceURL = null;
571 this.fileName = fileName;
572 }
573
574 /***
575 * Return the base path.
576 *
577 * @return the base path
578 * @see FileConfiguration#getBasePath()
579 */
580 public String getBasePath()
581 {
582 return basePath;
583 }
584
585 /***
586 * Sets the base path. The base path is typically either a path to a
587 * directory or a URL. Together with the value passed to the
588 * <code>setFileName()</code> method it defines the location of the
589 * configuration file to be loaded. The strategies for locating the file are
590 * quite tolerant. For instance if the file name is already an absolute path
591 * or a fully defined URL, the base path will be ignored. The base path can
592 * also be a URL, in which case the file name is interpreted in this URL's
593 * context. Because the base path is used by some of the derived classes for
594 * resolving relative file names it should contain a meaningful value. If
595 * other methods are used for determining the location of the configuration
596 * file (e.g. <code>setFile()</code> or <code>setURL()</code>), the
597 * base path is automatically set.
598 *
599 * @param basePath the base path.
600 */
601 public void setBasePath(String basePath)
602 {
603 sourceURL = null;
604 this.basePath = basePath;
605 }
606
607 /***
608 * Return the file where the configuration is stored. If the base path is a
609 * URL with a protocol different than "file", or the configuration
610 * file is within a compressed archive, the return value
611 * will not point to a valid file object.
612 *
613 * @return the file where the configuration is stored; this can be <b>null</b>
614 */
615 public File getFile()
616 {
617 if (getFileName() == null && sourceURL == null)
618 {
619 return null;
620 }
621 else if (sourceURL != null)
622 {
623 return ConfigurationUtils.fileFromURL(sourceURL);
624 }
625 else
626 {
627 return ConfigurationUtils.getFile(getBasePath(), getFileName());
628 }
629 }
630
631 /***
632 * Set the file where the configuration is stored. The passed in file is
633 * made absolute if it is not yet. Then the file's path component becomes
634 * the base path and its name component becomes the file name.
635 *
636 * @param file the file where the configuration is stored
637 */
638 public void setFile(File file)
639 {
640 sourceURL = null;
641 setFileName(file.getName());
642 setBasePath((file.getParentFile() != null) ? file.getParentFile()
643 .getAbsolutePath() : null);
644 }
645
646 /***
647 * Returns the full path to the file this configuration is based on. The
648 * return value is a valid File path only if this configuration is based on
649 * a file on the local disk.
650 * If the configuration was loaded from a packed archive the returned value
651 * is the string form of the URL from which the configuration was loaded.
652 *
653 * @return the full path to the configuration file
654 */
655 public String getPath()
656 {
657 String path = null;
658 File file = getFile();
659
660 if (file != null)
661 {
662 path = file.getAbsolutePath();
663 }
664
665
666 if (path == null)
667 {
668 if (sourceURL != null)
669 {
670 path = sourceURL.getPath();
671 }
672 else
673 {
674 try
675 {
676 path = ConfigurationUtils.getURL(getBasePath(), getFileName()).getPath();
677 }
678 catch (MalformedURLException e)
679 {
680
681 ;
682 }
683 }
684 }
685
686 return path;
687 }
688
689 /***
690 * Sets the location of this configuration as a full or relative path name.
691 * The passed in path should represent a valid file name on the file system.
692 * It must not be used to specify relative paths for files that exist
693 * in classpath, either plain file system or compressed archive,
694 * because this method expands any relative path to an absolute one which
695 * may end in an invalid absolute path for classpath references.
696 *
697 * @param path the full path name of the configuration file
698 */
699 public void setPath(String path)
700 {
701 setFile(new File(path));
702 }
703
704 /***
705 * Return the URL where the configuration is stored.
706 *
707 * @return the configuration's location as URL
708 */
709 public URL getURL()
710 {
711 return (sourceURL != null) ? sourceURL
712 : ConfigurationUtils.locate(getBasePath(), getFileName());
713 }
714
715 /***
716 * Set the location of this configuration as a URL. For loading this can be
717 * an arbitrary URL with a supported protocol. If the configuration is to
718 * be saved, too, a URL with the "file" protocol should be
719 * provided.
720 *
721 * @param url the location of this configuration as URL
722 */
723 public void setURL(URL url)
724 {
725 setBasePath(ConfigurationUtils.getBasePath(url));
726 setFileName(ConfigurationUtils.getFileName(url));
727 sourceURL = url;
728 }
729
730 public void setAutoSave(boolean autoSave)
731 {
732 this.autoSave = autoSave;
733 }
734
735 public boolean isAutoSave()
736 {
737 return autoSave;
738 }
739
740 /***
741 * Save the configuration if the automatic persistence is enabled
742 * and if a file is specified.
743 */
744 protected void possiblySave()
745 {
746 if (autoSave && fileName != null)
747 {
748 try
749 {
750 save();
751 }
752 catch (ConfigurationException e)
753 {
754 throw new ConfigurationRuntimeException("Failed to auto-save", e);
755 }
756 }
757 }
758
759 /***
760 * Adds a new property to this configuration. This implementation checks if
761 * the auto save mode is enabled and saves the configuration if necessary.
762 *
763 * @param key the key of the new property
764 * @param value the value
765 */
766 public void addProperty(String key, Object value)
767 {
768 super.addProperty(key, value);
769 possiblySave();
770 }
771
772 /***
773 * Sets a new value for the specified property. This implementation checks
774 * if the auto save mode is enabled and saves the configuration if
775 * necessary.
776 *
777 * @param key the key of the affected property
778 * @param value the value
779 */
780 public void setProperty(String key, Object value)
781 {
782 super.setProperty(key, value);
783 possiblySave();
784 }
785
786 public void clearProperty(String key)
787 {
788 super.clearProperty(key);
789 possiblySave();
790 }
791
792 public ReloadingStrategy getReloadingStrategy()
793 {
794 return strategy;
795 }
796
797 public void setReloadingStrategy(ReloadingStrategy strategy)
798 {
799 this.strategy = strategy;
800 strategy.setConfiguration(this);
801 strategy.init();
802 }
803
804 /***
805 * Performs a reload operation if necessary. This method is called on each
806 * access of this configuration. It asks the associated reloading strategy
807 * whether a reload should be performed. If this is the case, the
808 * configuration is cleared and loaded again from its source. If this
809 * operation causes an exception, the registered error listeners will be
810 * notified. The error event passed to the listeners is of type
811 * <code>EVENT_RELOAD</code> and contains the exception that caused the
812 * event.
813 */
814 public void reload()
815 {
816 synchronized (reloadLock)
817 {
818 if (noReload == 0)
819 {
820 try
821 {
822 enterNoReload();
823
824 if (strategy.reloadingRequired())
825 {
826 if (getLogger().isInfoEnabled())
827 {
828 getLogger().info("Reloading configuration. URL is " + getURL());
829 }
830 fireEvent(EVENT_RELOAD, null, getURL(), true);
831 setDetailEvents(false);
832 boolean autoSaveBak = this.isAutoSave();
833 this.setAutoSave(false);
834 try
835 {
836 clear();
837 load();
838 }
839 finally
840 {
841 this.setAutoSave(autoSaveBak);
842 setDetailEvents(true);
843 }
844 fireEvent(EVENT_RELOAD, null, getURL(), false);
845
846
847 strategy.reloadingPerformed();
848 }
849 }
850 catch (Exception e)
851 {
852 fireError(EVENT_RELOAD, null, null, e);
853
854 }
855 finally
856 {
857 exitNoReload();
858 }
859 }
860 }
861 }
862
863 /***
864 * Enters the "No reloading mode". As long as this mode is active
865 * no reloading will be performed. This is necessary for some
866 * implementations of <code>save()</code> in derived classes, which may
867 * cause a reload while accessing the properties to save. This may cause the
868 * whole configuration to be erased. To avoid this, this method can be
869 * called first. After a call to this method there always must be a
870 * corresponding call of <code>{@link #exitNoReload()}</code> later! (If
871 * necessary, <code>finally</code> blocks must be used to ensure this.
872 */
873 protected void enterNoReload()
874 {
875 synchronized (reloadLock)
876 {
877 noReload++;
878 }
879 }
880
881 /***
882 * Leaves the "No reloading mode".
883 *
884 * @see #enterNoReload()
885 */
886 protected void exitNoReload()
887 {
888 synchronized (reloadLock)
889 {
890 if (noReload > 0)
891 {
892 noReload--;
893 }
894 }
895 }
896
897 /***
898 * Sends an event to all registered listeners. This implementation ensures
899 * that no reloads are performed while the listeners are invoked. So
900 * infinite loops can be avoided that can be caused by event listeners
901 * accessing the configuration's properties when they are invoked.
902 *
903 * @param type the event type
904 * @param propName the name of the property
905 * @param propValue the value of the property
906 * @param before the before update flag
907 */
908 protected void fireEvent(int type, String propName, Object propValue, boolean before)
909 {
910 enterNoReload();
911 try
912 {
913 super.fireEvent(type, propName, propValue, before);
914 }
915 finally
916 {
917 exitNoReload();
918 }
919 }
920
921 public Object getProperty(String key)
922 {
923 synchronized (reloadLock)
924 {
925 reload();
926 return super.getProperty(key);
927 }
928 }
929
930 public boolean isEmpty()
931 {
932 reload();
933 return super.isEmpty();
934 }
935
936 public boolean containsKey(String key)
937 {
938 reload();
939 return super.containsKey(key);
940 }
941
942 /***
943 * Returns an <code>Iterator</code> with the keys contained in this
944 * configuration. This implementation performs a reload if necessary before
945 * obtaining the keys. The <code>Iterator</code> returned by this method
946 * points to a snapshot taken when this method was called. Later changes at
947 * the set of keys (including those caused by a reload) won't be visible.
948 * This is because a reload can happen at any time during iteration, and it
949 * is impossible to determine how this reload affects the current iteration.
950 * When using the iterator a client has to be aware that changes of the
951 * configuration are possible at any time. For instance, if after a reload
952 * operation some keys are no longer present, the iterator will still return
953 * those keys because they were found when it was created.
954 *
955 * @return an <code>Iterator</code> with the keys of this configuration
956 */
957 public Iterator getKeys()
958 {
959 reload();
960 List keyList = new LinkedList();
961 enterNoReload();
962 try
963 {
964 for (Iterator it = super.getKeys(); it.hasNext();)
965 {
966 keyList.add(it.next());
967 }
968
969 return keyList.iterator();
970 }
971 finally
972 {
973 exitNoReload();
974 }
975 }
976
977 /***
978 * Create the path to the specified file.
979 *
980 * @param file the target file
981 */
982 private void createPath(File file)
983 {
984 if (file != null)
985 {
986
987 if (!file.exists())
988 {
989 File parent = file.getParentFile();
990 if (parent != null && !parent.exists())
991 {
992 parent.mkdirs();
993 }
994 }
995 }
996 }
997
998 public String getEncoding()
999 {
1000 return encoding;
1001 }
1002
1003 public void setEncoding(String encoding)
1004 {
1005 this.encoding = encoding;
1006 }
1007
1008 /***
1009 * Creates a copy of this configuration. The new configuration object will
1010 * contain the same properties as the original, but it will lose any
1011 * connection to a source file (if one exists); this includes setting the
1012 * source URL, base path, and file name to <b>null</b>. This is done to
1013 * avoid race conditions if both the original and the copy are modified and
1014 * then saved.
1015 *
1016 * @return the copy
1017 * @since 1.3
1018 */
1019 public Object clone()
1020 {
1021 AbstractFileConfiguration copy = (AbstractFileConfiguration) super.clone();
1022 copy.setBasePath(null);
1023 copy.setFileName(null);
1024 copy.initReloadingStrategy();
1025 return copy;
1026 }
1027
1028 /***
1029 * Helper method for initializing the reloading strategy.
1030 */
1031 private void initReloadingStrategy()
1032 {
1033 setReloadingStrategy(new InvariantReloadingStrategy());
1034 }
1035
1036 /***
1037 * A helper method for closing an output stream. Occurring exceptions will
1038 * be ignored.
1039 *
1040 * @param out the output stream to be closed (may be <b>null</b>)
1041 * @since 1.5
1042 */
1043 private void closeSilent(OutputStream out)
1044 {
1045 try
1046 {
1047 if (out != null)
1048 {
1049 out.close();
1050 }
1051 }
1052 catch (IOException e)
1053 {
1054 getLogger().warn("Could not close output stream", e);
1055 }
1056 }
1057 }