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 037 /** 038 * <p>Base class for implementing file based hierarchical configurations.</p> 039 * <p>This class serves an analogous purpose as the 040 * <code>{@link AbstractFileConfiguration}</code> class for non hierarchical 041 * configurations. It behaves in exactly the same way, so please refer to the 042 * documentation of <code>AbstractFileConfiguration</code> for further details.</p> 043 * 044 * @since 1.2 045 * 046 * @author Emmanuel Bourg 047 * @version $Id: AbstractHierarchicalFileConfiguration.java 1158114 2011-08-16 06:04:18Z oheger $ 048 */ 049 public abstract class AbstractHierarchicalFileConfiguration 050 extends HierarchicalConfiguration 051 implements FileConfiguration, ConfigurationListener, ConfigurationErrorListener, FileSystemBased, 052 Reloadable 053 { 054 /** Stores the delegate used for implementing functionality related to the 055 * <code>FileConfiguration</code> interface. 056 */ 057 private FileConfigurationDelegate delegate; 058 059 /** 060 * Creates a new instance of 061 * <code>AbstractHierarchicalFileConfiguration</code>. 062 */ 063 protected AbstractHierarchicalFileConfiguration() 064 { 065 initialize(); 066 } 067 068 /** 069 * Creates a new instance of 070 * <code>AbstractHierarchicalFileConfiguration</code> 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 protected void addPropertyDirect(String key, Object obj) 143 { 144 synchronized (delegate.getReloadLock()) 145 { 146 super.addPropertyDirect(key, obj); 147 delegate.possiblySave(); 148 } 149 } 150 151 public void clearProperty(String key) 152 { 153 synchronized (delegate.getReloadLock()) 154 { 155 super.clearProperty(key); 156 delegate.possiblySave(); 157 } 158 } 159 160 public void clearTree(String key) 161 { 162 synchronized (delegate.getReloadLock()) 163 { 164 super.clearTree(key); 165 delegate.possiblySave(); 166 } 167 } 168 169 public void setProperty(String key, Object value) 170 { 171 synchronized (delegate.getReloadLock()) 172 { 173 super.setProperty(key, value); 174 delegate.possiblySave(); 175 } 176 } 177 178 public void load() throws ConfigurationException 179 { 180 delegate.load(); 181 } 182 183 public void load(String fileName) throws ConfigurationException 184 { 185 delegate.load(fileName); 186 } 187 188 public void load(File file) throws ConfigurationException 189 { 190 delegate.load(file); 191 } 192 193 public void load(URL url) throws ConfigurationException 194 { 195 delegate.load(url); 196 } 197 198 public void load(InputStream in) throws ConfigurationException 199 { 200 delegate.load(in); 201 } 202 203 public void load(InputStream in, String encoding) throws ConfigurationException 204 { 205 delegate.load(in, encoding); 206 } 207 208 public void save() throws ConfigurationException 209 { 210 delegate.save(); 211 } 212 213 public void save(String fileName) throws ConfigurationException 214 { 215 delegate.save(fileName); 216 } 217 218 public void save(File file) throws ConfigurationException 219 { 220 delegate.save(file); 221 } 222 223 public void save(URL url) throws ConfigurationException 224 { 225 delegate.save(url); 226 } 227 228 public void save(OutputStream out) throws ConfigurationException 229 { 230 delegate.save(out); 231 } 232 233 public void save(OutputStream out, String encoding) throws ConfigurationException 234 { 235 delegate.save(out, encoding); 236 } 237 238 public String getFileName() 239 { 240 return delegate.getFileName(); 241 } 242 243 public void setFileName(String fileName) 244 { 245 delegate.setFileName(fileName); 246 } 247 248 public String getBasePath() 249 { 250 return delegate.getBasePath(); 251 } 252 253 public void setBasePath(String basePath) 254 { 255 delegate.setBasePath(basePath); 256 } 257 258 public File getFile() 259 { 260 return delegate.getFile(); 261 } 262 263 public void setFile(File file) 264 { 265 delegate.setFile(file); 266 } 267 268 public URL getURL() 269 { 270 return delegate.getURL(); 271 } 272 273 public void setURL(URL url) 274 { 275 delegate.setURL(url); 276 } 277 278 public void setAutoSave(boolean autoSave) 279 { 280 delegate.setAutoSave(autoSave); 281 } 282 283 public boolean isAutoSave() 284 { 285 return delegate.isAutoSave(); 286 } 287 288 public ReloadingStrategy getReloadingStrategy() 289 { 290 return delegate.getReloadingStrategy(); 291 } 292 293 public void setReloadingStrategy(ReloadingStrategy strategy) 294 { 295 delegate.setReloadingStrategy(strategy); 296 } 297 298 public void reload() 299 { 300 reload(false); 301 } 302 303 private boolean reload(boolean checkReload) 304 { 305 synchronized (delegate.getReloadLock()) 306 { 307 setDetailEvents(false); 308 try 309 { 310 return delegate.reload(checkReload); 311 } 312 finally 313 { 314 setDetailEvents(true); 315 } 316 } 317 } 318 319 /** 320 * Reloads the associated configuration file. This method first clears the 321 * content of this configuration, then the associated configuration file is 322 * loaded again. Updates on this configuration which have not yet been saved 323 * are lost. Calling this method is like invoking <code>reload()</code> 324 * without checking the reloading strategy. 325 * 326 * @throws ConfigurationException if an error occurs 327 * @since 1.7 328 */ 329 public void refresh() throws ConfigurationException 330 { 331 delegate.refresh(); 332 } 333 334 public String getEncoding() 335 { 336 return delegate.getEncoding(); 337 } 338 339 public void setEncoding(String encoding) 340 { 341 delegate.setEncoding(encoding); 342 } 343 344 public Object getReloadLock() 345 { 346 return delegate.getReloadLock(); 347 } 348 349 public boolean containsKey(String key) 350 { 351 reload(); 352 synchronized (delegate.getReloadLock()) 353 { 354 return super.containsKey(key); 355 } 356 } 357 358 public Iterator getKeys() 359 { 360 reload(); 361 synchronized (delegate.getReloadLock()) 362 { 363 return super.getKeys(); 364 } 365 } 366 367 public Iterator getKeys(String prefix) 368 { 369 reload(); 370 synchronized (delegate.getReloadLock()) 371 { 372 return super.getKeys(prefix); 373 } 374 } 375 376 public Object getProperty(String key) 377 { 378 if (reload(true)) 379 { 380 // Avoid reloading again and getting the same error. 381 synchronized (delegate.getReloadLock()) 382 { 383 return super.getProperty(key); 384 } 385 } 386 return null; 387 } 388 389 public boolean isEmpty() 390 { 391 reload(); 392 synchronized (delegate.getReloadLock()) 393 { 394 return super.isEmpty(); 395 } 396 } 397 398 /** 399 * Directly adds sub nodes to this configuration. This implementation checks 400 * whether auto save is necessary after executing the operation. 401 * 402 * @param key the key where the nodes are to be added 403 * @param nodes a collection with the nodes to be added 404 * @since 1.5 405 */ 406 public void addNodes(String key, Collection nodes) 407 { 408 synchronized (delegate.getReloadLock()) 409 { 410 super.addNodes(key, nodes); 411 delegate.possiblySave(); 412 } 413 } 414 415 /** 416 * Fetches a list of nodes, which are selected by the specified key. This 417 * implementation will perform a reload if necessary. 418 * 419 * @param key the key 420 * @return a list with the selected nodes 421 */ 422 protected List fetchNodeList(String key) 423 { 424 reload(); 425 synchronized (delegate.getReloadLock()) 426 { 427 return super.fetchNodeList(key); 428 } 429 } 430 431 /** 432 * Reacts on changes of an associated subnode configuration. If the auto 433 * save mechanism is active, the configuration must be saved. 434 * 435 * @param event the event describing the change 436 * @since 1.5 437 */ 438 protected void subnodeConfigurationChanged(ConfigurationEvent event) 439 { 440 delegate.possiblySave(); 441 super.subnodeConfigurationChanged(event); 442 } 443 444 /** 445 * Creates the file configuration delegate, i.e. the object that implements 446 * functionality required by the <code>FileConfiguration</code> interface. 447 * This base implementation will return an instance of the 448 * <code>FileConfigurationDelegate</code> class. Derived classes may 449 * override it to create a different delegate object. 450 * 451 * @return the file configuration delegate 452 */ 453 protected FileConfigurationDelegate createDelegate() 454 { 455 return new FileConfigurationDelegate(); 456 } 457 458 /** 459 * Helper method for initializing the file configuration delegate. 460 * 461 * @param del the delegate 462 */ 463 private void initDelegate(FileConfigurationDelegate del) 464 { 465 del.addConfigurationListener(this); 466 del.addErrorListener(this); 467 del.setLogger(getLogger()); 468 } 469 470 /** 471 * Reacts on configuration change events triggered by the delegate. These 472 * events are passed to the registered configuration listeners. 473 * 474 * @param event the triggered event 475 * @since 1.3 476 */ 477 public void configurationChanged(ConfigurationEvent event) 478 { 479 // deliver reload events to registered listeners 480 setDetailEvents(true); 481 try 482 { 483 fireEvent(event.getType(), event.getPropertyName(), event 484 .getPropertyValue(), event.isBeforeUpdate()); 485 } 486 finally 487 { 488 setDetailEvents(false); 489 } 490 } 491 492 public void configurationError(ConfigurationErrorEvent event) 493 { 494 fireError(event.getType(), event.getPropertyName(), event.getPropertyValue(), 495 event.getCause()); 496 } 497 498 /** 499 * Returns the file configuration delegate. 500 * 501 * @return the delegate 502 */ 503 protected FileConfigurationDelegate getDelegate() 504 { 505 return delegate; 506 } 507 508 /** 509 * Allows to set the file configuration delegate. 510 * @param delegate the new delegate 511 */ 512 protected void setDelegate(FileConfigurationDelegate delegate) 513 { 514 this.delegate = delegate; 515 } 516 517 /** 518 * Set the FileSystem to be used for this Configuration. 519 * @param fileSystem The FileSystem to use. 520 */ 521 public void setFileSystem(FileSystem fileSystem) 522 { 523 delegate.setFileSystem(fileSystem); 524 } 525 526 /** 527 * Reset the FileSystem to the default; 528 */ 529 public void resetFileSystem() 530 { 531 delegate.resetFileSystem(); 532 } 533 534 /** 535 * Retrieve the FileSystem being used. 536 * @return The FileSystem. 537 */ 538 public FileSystem getFileSystem() 539 { 540 return delegate.getFileSystem(); 541 } 542 543 /** 544 * A special implementation of the <code>FileConfiguration</code> interface that is 545 * used internally to implement the <code>FileConfiguration</code> methods 546 * for hierarchical configurations. 547 */ 548 protected class FileConfigurationDelegate extends AbstractFileConfiguration 549 { 550 public void load(Reader in) throws ConfigurationException 551 { 552 AbstractHierarchicalFileConfiguration.this.load(in); 553 } 554 555 public void save(Writer out) throws ConfigurationException 556 { 557 AbstractHierarchicalFileConfiguration.this.save(out); 558 } 559 560 public void clear() 561 { 562 AbstractHierarchicalFileConfiguration.this.clear(); 563 } 564 } 565 }