/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.commons.configuration2.reloading;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;

import org.apache.commons.configuration2.io.FileHandler;
import org.apache.commons.configuration2.io.FileLocatorUtils;

A specialized implementation of ReloadingDetector which monitors a file specified by a FileHandler.

An instance of this class is passed a FileHandler at construction time. Each time the isReloadingRequired() method is called, it checks whether the FileHandler points to a valid location. If this is the case, the file's last modification time is obtained and compared with the last stored time. If it has changed, a reload operation should be performed.

Because file I/O may be expensive it is possible to configure a refresh delay as a time in milliseconds. This is the minimum interval between two checks. If the isReloadingRequired() method is called in shorter intervals, it does not perform a check, but directly returns false.

To initialize an instance either isReloadingRequired() or reloadingPerformed() can be called. The first call of isReloadingRequired does not perform a check, but obtains the initial modification date of the monitored file. reloadingPerformed() always obtains the file's modification date and stores it internally.

Since:2.0
/** * <p> * A specialized implementation of {@code ReloadingDetector} which monitors a * file specified by a {@link FileHandler}. * </p> * <p> * An instance of this class is passed a {@code FileHandler} at construction * time. Each time the {@code isReloadingRequired()} method is called, it checks * whether the {@code FileHandler} points to a valid location. If this is the * case, the file's last modification time is obtained and compared with the * last stored time. If it has changed, a reload operation should be performed. * </p> * <p> * Because file I/O may be expensive it is possible to configure a refresh delay * as a time in milliseconds. This is the minimum interval between two checks. * If the {@code isReloadingRequired()} method is called in shorter intervals, * it does not perform a check, but directly returns <b>false</b>. * </p> * <p> * To initialize an instance either {@code isReloadingRequired()} or * {@code reloadingPerformed()} can be called. The first call of * {@code isReloadingRequired} does not perform a check, but obtains the initial * modification date of the monitored file. {@code reloadingPerformed()} always * obtains the file's modification date and stores it internally. * </p> * * @since 2.0 */
public class FileHandlerReloadingDetector implements ReloadingDetector {
Constant for the jar URL protocol.
/** Constant for the jar URL protocol. */
private static final String JAR_PROTOCOL = "jar";
Constant for the default refresh delay.
/** Constant for the default refresh delay. */
private static final int DEFAULT_REFRESH_DELAY = 5000;
The associated file handler.
/** The associated file handler. */
private final FileHandler fileHandler;
The refresh delay.
/** The refresh delay. */
private final long refreshDelay;
The last time the configuration file was modified.
/** The last time the configuration file was modified. */
private long lastModified;
The last time the file was checked for changes.
/** The last time the file was checked for changes. */
private long lastChecked;
Creates a new instance of FileHandlerReloadingDetector and initializes it with the FileHandler to monitor and the refresh delay. The handler is directly used, no copy is created. So it is possible to change the location monitored by manipulating the FileHandler object.
Params:
  • handler – the FileHandler associated with this detector (can be null)
  • refreshDelay – the refresh delay; a value of 0 means that a check is performed in all cases
/** * Creates a new instance of {@code FileHandlerReloadingDetector} and * initializes it with the {@code FileHandler} to monitor and the refresh * delay. The handler is directly used, no copy is created. So it is * possible to change the location monitored by manipulating the * {@code FileHandler} object. * * @param handler the {@code FileHandler} associated with this detector (can * be <b>null</b>) * @param refreshDelay the refresh delay; a value of 0 means that a check is * performed in all cases */
public FileHandlerReloadingDetector(final FileHandler handler, final long refreshDelay) { fileHandler = handler != null ? handler : new FileHandler(); this.refreshDelay = refreshDelay; }
Creates a new instance of FileHandlerReloadingDetector and initializes it with the FileHandler to monitor and a default refresh delay.
Params:
  • handler – the FileHandler associated with this detector (can be null)
/** * Creates a new instance of {@code FileHandlerReloadingDetector} and * initializes it with the {@code FileHandler} to monitor and a default * refresh delay. * * @param handler the {@code FileHandler} associated with this detector (can * be <b>null</b>) */
public FileHandlerReloadingDetector(final FileHandler handler) { this(handler, DEFAULT_REFRESH_DELAY); }
Creates a new instance of FileHandlerReloadingDetector with an uninitialized FileHandler object. The file to be monitored has to be set later by manipulating the handler object returned by getFileHandler().
/** * Creates a new instance of {@code FileHandlerReloadingDetector} with an * uninitialized {@code FileHandler} object. The file to be monitored has to * be set later by manipulating the handler object returned by * {@code getFileHandler()}. */
public FileHandlerReloadingDetector() { this(null); }
Returns the FileHandler associated with this object. The underlying handler is directly returned, so changing its location also changes the file monitored by this detector.
Returns:the associated FileHandler
/** * Returns the {@code FileHandler} associated with this object. The * underlying handler is directly returned, so changing its location also * changes the file monitored by this detector. * * @return the associated {@code FileHandler} */
public FileHandler getFileHandler() { return fileHandler; }
Returns the refresh delay. This is a time in milliseconds. The isReloadingRequired() method first checks whether the time since the previous check is more than this value in the past. Otherwise, no check is performed. This is a means to limit file I/O caused by this class.
Returns:the refresh delay used by this object
/** * Returns the refresh delay. This is a time in milliseconds. The * {@code isReloadingRequired()} method first checks whether the time since * the previous check is more than this value in the past. Otherwise, no * check is performed. This is a means to limit file I/O caused by this * class. * * @return the refresh delay used by this object */
public long getRefreshDelay() { return refreshDelay; }
{@inheritDoc} This implementation checks whether the associated FileHandler points to a valid file and whether the last modification time of this time has changed since the last check. The refresh delay is taken into account, too; a check is only performed if at least this time has passed since the last check.
/** * {@inheritDoc} This implementation checks whether the associated * {@link FileHandler} points to a valid file and whether the last * modification time of this time has changed since the last check. The * refresh delay is taken into account, too; a check is only performed if at * least this time has passed since the last check. */
@Override public boolean isReloadingRequired() { final long now = System.currentTimeMillis(); if (now >= lastChecked + getRefreshDelay()) { lastChecked = now; final long modified = getLastModificationDate(); if (modified > 0) { if (lastModified == 0) { // initialization updateLastModified(modified); } else { if (modified != lastModified) { return true; } } } } return false; }
{@inheritDoc} This implementation updates the internally stored last modification date with the current modification date of the monitored file. So the next change is detected when this file is changed again.
/** * {@inheritDoc} This implementation updates the internally stored last * modification date with the current modification date of the monitored * file. So the next change is detected when this file is changed again. */
@Override public void reloadingPerformed() { updateLastModified(getLastModificationDate()); }
Tells this implementation that the internally stored state should be refreshed. This method is intended to be called after the creation of an instance.
/** * Tells this implementation that the internally stored state should be * refreshed. This method is intended to be called after the creation * of an instance. */
public void refresh() { updateLastModified(getLastModificationDate()); }
Returns the date of the last modification of the monitored file. A return value of 0 indicates, that the monitored file does not exist.
Returns:the last modification date
/** * Returns the date of the last modification of the monitored file. A return * value of 0 indicates, that the monitored file does not exist. * * @return the last modification date */
protected long getLastModificationDate() { final File file = getExistingFile(); return file != null ? file.lastModified() : 0; }
Updates the last modification date of the monitored file. The need for a reload is detected only if the file's modification date is different from this value.
Params:
  • time – the new last modification date
/** * Updates the last modification date of the monitored file. The need for a * reload is detected only if the file's modification date is different from * this value. * * @param time the new last modification date */
protected void updateLastModified(final long time) { lastModified = time; }
Returns the File object which is monitored by this object. This method is called every time the file's last modification time is needed. If it returns null, no check is performed. This base implementation obtains the File from the associated FileHandler. It can also deal with URLs to jar files.
Returns:the File to be monitored (can be null)
/** * Returns the {@code File} object which is monitored by this object. This * method is called every time the file's last modification time is needed. * If it returns <b>null</b>, no check is performed. This base * implementation obtains the {@code File} from the associated * {@code FileHandler}. It can also deal with URLs to jar files. * * @return the {@code File} to be monitored (can be <b>null</b>) */
protected File getFile() { final URL url = getFileHandler().getURL(); return url != null ? fileFromURL(url) : getFileHandler().getFile(); }
Returns the monitored File or null if it does not exist.
Returns:the monitored File or null
/** * Returns the monitored {@code File} or <b>null</b> if it does not exist. * * @return the monitored {@code File} or <b>null</b> */
private File getExistingFile() { File file = getFile(); if (file != null && !file.exists()) { file = null; } return file; }
Helper method for transforming a URL into a file object. This method handles file: and jar: URLs.
Params:
  • url – the URL to be converted
Returns:the resulting file or null
/** * Helper method for transforming a URL into a file object. This method * handles file: and jar: URLs. * * @param url the URL to be converted * @return the resulting file or <b>null </b> */
private static File fileFromURL(final URL url) { if (JAR_PROTOCOL.equals(url.getProtocol())) { final String path = url.getPath(); try { return FileLocatorUtils.fileFromURL(new URL(path.substring(0, path.indexOf('!')))); } catch (final MalformedURLException mex) { return null; } } return FileLocatorUtils.fileFromURL(url); } }