##### Coverage Report - org.apache.commons.configuration.AbstractFileConfiguration

Classes in this File | Line Coverage | Branch Coverage | Complexity
AbstractFileConfiguration |
90% | 234/261
97% | 33/34
| 2.4375;2,438

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