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