ReloadingController.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.reloading;

  18. import org.apache.commons.configuration2.event.Event;
  19. import org.apache.commons.configuration2.event.EventListener;
  20. import org.apache.commons.configuration2.event.EventListenerList;
  21. import org.apache.commons.configuration2.event.EventSource;
  22. import org.apache.commons.configuration2.event.EventType;

  23. /**
  24.  * <p>
  25.  * A class for adding support for reload operations in a generic way.
  26.  * </p>
  27.  * <p>
  28.  * A {@code ReloadingController} monitors a specific source and triggers reloading events if necessary. So it does not
  29.  * perform reloading itself, but only sends out notifications when it thinks that this should be done. This allows for a
  30.  * very generic setup in which different components involved in reloading are loosely coupled via events.
  31.  * </p>
  32.  * <p>
  33.  * A typical usage scenario is as follows:
  34.  * </p>
  35.  * <ul>
  36.  * <li>A {@code ReloadingController} instance is created and initialized with a {@link ReloadingDetector} object.</li>
  37.  * <li>A number of {@link EventListener} objects for reloading events can be registered at the controller.</li>
  38.  * <li>Now the controller's {@code checkForReloading()} method is called whenever a check is to be performed. This could
  39.  * be done for instance by a timer in regular intervals or by any other means appropriate for a specific
  40.  * application.</li>
  41.  * <li>When a check reveals that a reload operation is necessary all registered event listeners are notified.</li>
  42.  * <li>Typically one of the listeners is responsible to perform the actual reload operation. (How this is done is not in
  43.  * the scope of the controller object.) After this has been done, the controller's {@code resetReloadingState()} method
  44.  * must be called. It tells the controller that the last notification has been processed and that new checks are
  45.  * possible again. It is important that this method is called. Otherwise, {@code checkForReloading()} will not do any
  46.  * new checks or send out event notifications any more.</li>
  47.  * </ul>
  48.  * <p>
  49.  * This class can be accessed from multiple threads concurrently. It shields the associated {@link ReloadingDetector}
  50.  * object for concurrent access, so that a concrete detector implementation does not have to be thread-safe.
  51.  * </p>
  52.  *
  53.  * @since 2.0
  54.  */
  55. public class ReloadingController implements EventSource {
  56.     /** Stores a reference to the reloading detector. */
  57.     private final ReloadingDetector detector;

  58.     /** The helper object which manages the registered event listeners. */
  59.     private final EventListenerList listeners;

  60.     /** A flag whether this controller is in reloading state. */
  61.     private boolean reloadingState;

  62.     /**
  63.      * Creates a new instance of {@code ReloadingController} and associates it with the given {@code ReloadingDetector}
  64.      * object.
  65.      *
  66.      * @param detect the {@code ReloadingDetector} (must not be <strong>null</strong>)
  67.      * @throws IllegalArgumentException if the detector is undefined
  68.      */
  69.     public ReloadingController(final ReloadingDetector detect) {
  70.         if (detect == null) {
  71.             throw new IllegalArgumentException("ReloadingDetector must not be null!");
  72.         }

  73.         detector = detect;
  74.         listeners = new EventListenerList();
  75.     }

  76.     /**
  77.      * {@inheritDoc} This class generates events of type {@code ReloadingEvent}.
  78.      */
  79.     @Override
  80.     public <T extends Event> void addEventListener(final EventType<T> eventType, final EventListener<? super T> listener) {
  81.         listeners.addEventListener(eventType, listener);
  82.     }

  83.     /**
  84.      * Performs a check whether a reload operation is necessary. This method has to be called to trigger the generation of
  85.      * reloading events. It delegates to the associated {@link ReloadingDetector} and sends out notifications if necessary.
  86.      * The argument can be an arbitrary data object; it will be part of the event notification sent out when a reload
  87.      * operation should be performed. The return value indicates whether a change was detected and an event was sent. Once a
  88.      * need for a reload is detected, this controller is in <em>reloading state</em>. Until this state is reset (by calling
  89.      * {@link #resetReloadingState()}), no further reloading checks are performed by this method, and no events are fired;
  90.      * it then returns always <strong>true</strong>.
  91.      *
  92.      * @param data additional data for an event notification
  93.      * @return a flag whether a reload operation is necessary
  94.      */
  95.     public boolean checkForReloading(final Object data) {
  96.         boolean sendEvent = false;
  97.         synchronized (this) {
  98.             if (isInReloadingState()) {
  99.                 return true;
  100.             }
  101.             if (getDetector().isReloadingRequired()) {
  102.                 sendEvent = true;
  103.                 reloadingState = true;
  104.             }
  105.         }

  106.         if (sendEvent) {
  107.             listeners.fire(new ReloadingEvent(this, data));
  108.             return true;
  109.         }
  110.         return false;
  111.     }

  112.     /**
  113.      * Gets the {@code ReloadingDetector} used by this controller.
  114.      *
  115.      * @return the {@code ReloadingDetector}
  116.      */
  117.     public ReloadingDetector getDetector() {
  118.         return detector;
  119.     }

  120.     /**
  121.      * Tests whether this controller is in <em>reloading state</em>. A return value of <strong>true</strong> means that a previous
  122.      * invocation of {@code checkForReloading()} has detected the necessity for a reload operation, but
  123.      * {@code resetReloadingState()} has not been called yet. In this state no further reloading checks are possible.
  124.      *
  125.      * @return a flag whether this controller is in reloading state
  126.      */
  127.     public synchronized boolean isInReloadingState() {
  128.         return reloadingState;
  129.     }

  130.     @Override
  131.     public <T extends Event> boolean removeEventListener(final EventType<T> eventType, final EventListener<? super T> listener) {
  132.         return listeners.removeEventListener(eventType, listener);
  133.     }

  134.     /**
  135.      * Resets the reloading state. This tells the controller that reloading has been performed and new checks are possible
  136.      * again. If this controller is not in reloading state, this method has no effect.
  137.      */
  138.     public synchronized void resetReloadingState() {
  139.         if (isInReloadingState()) {
  140.             getDetector().reloadingPerformed();
  141.             reloadingState = false;
  142.         }
  143.     }
  144. }