AutoSaveListener.java

  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. package org.apache.commons.configuration2.builder;

  18. import org.apache.commons.configuration2.event.ConfigurationEvent;
  19. import org.apache.commons.configuration2.event.EventListener;
  20. import org.apache.commons.configuration2.ex.ConfigurationException;
  21. import org.apache.commons.configuration2.io.FileHandler;
  22. import org.apache.commons.configuration2.io.FileHandlerListenerAdapter;
  23. import org.apache.commons.logging.Log;
  24. import org.apache.commons.logging.LogFactory;

  25. /**
  26.  * <p>
  27.  * A listener class implementing an auto save mechanism for file-based configurations.
  28.  * </p>
  29.  * <p>
  30.  * Instances of this class are used by {@link FileBasedConfigurationBuilder} to save their managed configuration
  31.  * instances when they are changed. Objects are registered at {@code Configuration} objects as event listeners and thus
  32.  * can trigger save operations whenever a change event is received.
  33.  * </p>
  34.  * <p>
  35.  * There is one complication however: Some configuration implementations fire change events during a load operation.
  36.  * Such events must be ignored to prevent corruption of the source file. This is achieved by monitoring the associated
  37.  * {@code FileHandler}: during load operations no auto-save is performed.
  38.  * </p>
  39.  *
  40.  * @since 2.0
  41.  */
  42. final class AutoSaveListener extends FileHandlerListenerAdapter implements EventListener<ConfigurationEvent> {
  43.     /** The logger. */
  44.     private final Log log = LogFactory.getLog(getClass());

  45.     /** The associated builder. */
  46.     private final FileBasedConfigurationBuilder<?> builder;

  47.     /** Stores the file handler monitored by this listener. */
  48.     private FileHandler handler;

  49.     /**
  50.      * A counter to keep track whether a load operation is currently in progress.
  51.      */
  52.     private int loading;

  53.     /**
  54.      * Creates a new instance of {@code AutoSaveListener} and initializes it with the associated builder.
  55.      *
  56.      * @param bldr the associated builder
  57.      */
  58.     public AutoSaveListener(final FileBasedConfigurationBuilder<?> bldr) {
  59.         builder = bldr;
  60.     }

  61.     /**
  62.      * Checks whether an auto save operation has to be performed based on the passed in event and the current state of this
  63.      * object.
  64.      *
  65.      * @param event the configuration change event
  66.      * @return <strong>true</strong> if a save operation should be performed, <strong>false</strong> otherwise
  67.      */
  68.     private boolean autoSaveRequired(final ConfigurationEvent event) {
  69.         return !event.isBeforeUpdate() && !inLoadOperation();
  70.     }

  71.     /**
  72.      * Returns a flag whether a load operation is currently in progress.
  73.      *
  74.      * @return a flag whether a load operation is in progress
  75.      */
  76.     private synchronized boolean inLoadOperation() {
  77.         return loading > 0;
  78.     }

  79.     /**
  80.      * {@inheritDoc} This implementation decrements the counter for load operations in progress.
  81.      */
  82.     @Override
  83.     public synchronized void loaded(final FileHandler handler) {
  84.         loading--;
  85.     }

  86.     /**
  87.      * {@inheritDoc} This implementation increments the counter for load operations in progress.
  88.      */
  89.     @Override
  90.     public synchronized void loading(final FileHandler handler) {
  91.         loading++;
  92.     }

  93.     /**
  94.      * {@inheritDoc} This implementation checks whether an auto-safe operation should be performed. This is the case if the
  95.      * event indicates that an update of the configuration has been performed and currently no load operation is in
  96.      * progress.
  97.      */
  98.     @Override
  99.     public void onEvent(final ConfigurationEvent event) {
  100.         if (autoSaveRequired(event)) {
  101.             try {
  102.                 builder.save();
  103.             } catch (final ConfigurationException ce) {
  104.                 log.warn("Auto save failed!", ce);
  105.             }
  106.         }
  107.     }

  108.     /**
  109.      * Updates the {@code FileHandler}. This method is called by the builder when a new configuration instance was created
  110.      * which is associated with a new file handler. It updates the internal file handler reference and performs necessary
  111.      * listener registrations.
  112.      *
  113.      * @param fh the new {@code FileHandler} (can be <strong>null</strong>)
  114.      */
  115.     public synchronized void updateFileHandler(final FileHandler fh) {
  116.         if (handler != null) {
  117.             handler.removeFileHandlerListener(this);
  118.         }

  119.         if (fh != null) {
  120.             fh.addFileHandlerListener(this);
  121.         }
  122.         handler = fh;
  123.     }
  124. }