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