/*
* 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.configuration.reloading;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import org.apache.commons.configuration.ConfigurationUtils;
import org.apache.commons.configuration.FileConfiguration;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* <p>A reloading strategy that will reload the configuration every time its
* underlying file is changed.</p>
* <p>This reloading strategy does not actively monitor a configuration file,
* but is triggered by its associated configuration whenever properties are
* accessed. It then checks the configuration file's last modification date
* and causes a reload if this has changed.</p>
* <p>To avoid permanent disc access on successive property lookups a refresh
* delay can be specified. This has the effect that the configuration file's
* last modification date is only checked once in this delay period. The default
* value for this refresh delay is 5 seconds.</p>
* <p>This strategy only works with FileConfiguration instances.</p>
*
* @author Emmanuel Bourg
* @version $Revision: 823891 $, $Date: 2009-10-10 19:17:44 +0200 (Sa, 10. Okt 2009) $
* @since 1.1
*/
public class FileChangedReloadingStrategy implements ReloadingStrategy
{
/** Constant for the jar URL protocol.*/
private static final String JAR_PROTOCOL = "jar";
/** Constant for the default refresh delay.*/
private static final int DEFAULT_REFRESH_DELAY = 5000;
/** Stores a reference to the configuration to be monitored.*/
protected FileConfiguration configuration;
/** The last time the configuration file was modified. */
protected long lastModified;
/** The last time the file was checked for changes. */
protected long lastChecked;
/** The minimum delay in milliseconds between checks. */
protected long refreshDelay = DEFAULT_REFRESH_DELAY;
/** A flag whether a reload is required.*/
private boolean reloading;
/** The Log to use for diagnostic messages */
private Log logger = LogFactory.getLog(FileChangedReloadingStrategy.class);
public void setConfiguration(FileConfiguration configuration)
{
this.configuration = configuration;
}
public void init()
{
updateLastModified();
}
public boolean reloadingRequired()
{
if (!reloading)
{
long now = System.currentTimeMillis();
if (now > lastChecked + refreshDelay)
{
lastChecked = now;
if (hasChanged())
{
if (logger.isDebugEnabled())
{
logger.debug("File change detected: " + getName());
}
reloading = true;
}
}
}
return reloading;
}
public void reloadingPerformed()
{
updateLastModified();
}
/**
* Return the minimal time in milliseconds between two reloadings.
*
* @return the refresh delay (in milliseconds)
*/
public long getRefreshDelay()
{
return refreshDelay;
}
/**
* Set the minimal time between two reloadings.
*
* @param refreshDelay refresh delay in milliseconds
*/
public void setRefreshDelay(long refreshDelay)
{
this.refreshDelay = refreshDelay;
}
/**
* Update the last modified time.
*/
protected void updateLastModified()
{
File file = getFile();
if (file != null)
{
lastModified = file.lastModified();
}
reloading = false;
}
/**
* Check if the configuration has changed since the last time it was loaded.
*
* @return a flag whether the configuration has changed
*/
protected boolean hasChanged()
{
File file = getFile();
if (file == null || !file.exists())
{
if (logger.isWarnEnabled() && lastModified != 0)
{
logger.warn("File was deleted: " + getName(file));
lastModified = 0;
}
return false;
}
return file.lastModified() > lastModified;
}
/**
* Returns the file that is monitored by this strategy. Note that the return
* value can be <b>null </b> under some circumstances.
*
* @return the monitored file
*/
protected File getFile()
{
return (configuration.getURL() != null) ? fileFromURL(configuration
.getURL()) : configuration.getFile();
}
/**
* 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 File fileFromURL(URL url)
{
if (JAR_PROTOCOL.equals(url.getProtocol()))
{
String path = url.getPath();
try
{
return ConfigurationUtils.fileFromURL(new URL(path.substring(0,
path.indexOf('!'))));
}
catch (MalformedURLException mex)
{
return null;
}
}
else
{
return ConfigurationUtils.fileFromURL(url);
}
}
private String getName()
{
return getName(getFile());
}
private String getName(File file)
{
String name = configuration.getURL().toString();
if (name == null)
{
if (file != null)
{
name = file.getAbsolutePath();
}
else
{
name = "base: " + configuration.getBasePath()
+ "file: " + configuration.getFileName();
}
}
return name;
}
}
|