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
018package org.apache.commons.configuration.reloading;
019
020import org.apache.commons.configuration.ConfigurationRuntimeException;
021import org.apache.commons.configuration.FileConfiguration;
022import org.apache.commons.configuration.FileSystem;
023import org.apache.commons.configuration.FileSystemBased;
024import org.apache.commons.logging.Log;
025import org.apache.commons.logging.LogFactory;
026import org.apache.commons.vfs2.FileObject;
027import org.apache.commons.vfs2.FileSystemException;
028import org.apache.commons.vfs2.FileSystemManager;
029import org.apache.commons.vfs2.VFS;
030
031/**
032 * <p>
033 * A file-based reloading strategy that uses <a
034 * href="http://commons.apache.org/vfs/">Commons VFS</a> to determine when a
035 * file was changed.
036 * </p>
037 * <p>
038 * This reloading strategy is very similar to
039 * {@link FileChangedReloadingStrategy}, except for the fact that it uses VFS
040 * and thus can deal with a variety of different configuration sources.
041 * </p>
042 * <p>
043 * This strategy only works with FileConfiguration instances.
044 * </p>
045 *
046 * @author <a
047 *         href="http://commons.apache.org/configuration/team-list.html">Commons
048 *         Configuration team</a>
049 * @version $Id: VFSFileChangedReloadingStrategy.html 901729 2014-03-15 20:24:09Z oheger $
050 * @since 1.7
051 */
052public class VFSFileChangedReloadingStrategy implements ReloadingStrategy
053{
054    /** Constant for the default refresh delay.*/
055    private static final int DEFAULT_REFRESH_DELAY = 5000;
056
057    /** Stores a reference to the configuration to be monitored.*/
058    protected FileConfiguration configuration;
059
060    /** The last time the configuration file was modified. */
061    protected long lastModified;
062
063    /** The last time the file was checked for changes. */
064    protected long lastChecked;
065
066    /** The minimum delay in milliseconds between checks. */
067    protected long refreshDelay = DEFAULT_REFRESH_DELAY;
068
069    /** A flag whether a reload is required.*/
070    private boolean reloading;
071
072    /** Stores the logger.*/
073    private Log log = LogFactory.getLog(getClass());
074
075    public void setConfiguration(FileConfiguration configuration)
076    {
077        this.configuration = configuration;
078    }
079
080    public void init()
081    {
082        if (configuration.getURL() == null && configuration.getFileName() == null)
083        {
084            return;
085        }
086        if (this.configuration == null)
087        {
088            throw new IllegalStateException("No configuration has been set for this strategy");
089        }
090        updateLastModified();
091    }
092
093    public boolean reloadingRequired()
094    {
095        if (!reloading)
096        {
097            long now = System.currentTimeMillis();
098
099            if (now > lastChecked + refreshDelay)
100            {
101                lastChecked = now;
102                if (hasChanged())
103                {
104                    reloading = true;
105                }
106            }
107        }
108
109        return reloading;
110    }
111
112    public void reloadingPerformed()
113    {
114        updateLastModified();
115    }
116
117    /**
118     * Return the minimal time in milliseconds between two reloadings.
119     *
120     * @return the refresh delay (in milliseconds)
121     */
122    public long getRefreshDelay()
123    {
124        return refreshDelay;
125    }
126
127    /**
128     * Set the minimal time between two reloadings.
129     *
130     * @param refreshDelay refresh delay in milliseconds
131     */
132    public void setRefreshDelay(long refreshDelay)
133    {
134        this.refreshDelay = refreshDelay;
135    }
136
137    /**
138     * Update the last modified time.
139     */
140    protected void updateLastModified()
141    {
142        FileObject file = getFile();
143        if (file != null)
144        {
145            try
146            {
147                lastModified = file.getContent().getLastModifiedTime();
148            }
149            catch (FileSystemException fse)
150            {
151                log.error("Unable to get last modified time for" + file.getName().getURI());
152            }
153        }
154        reloading = false;
155    }
156
157    /**
158     * Check if the configuration has changed since the last time it was loaded.
159     *
160     * @return a flag whether the configuration has changed
161     */
162    protected boolean hasChanged()
163    {
164        FileObject file = getFile();
165        try
166        {
167            if (file == null || !file.exists())
168            {
169                return false;
170            }
171
172            return file.getContent().getLastModifiedTime() > lastModified;
173        }
174        catch (FileSystemException ex)
175        {
176            log.error("Unable to get last modified time for" + file.getName().getURI());
177            return false;
178        }
179    }
180
181    /**
182     * Returns the file that is monitored by this strategy. Note that the return
183     * value can be <b>null </b> under some circumstances.
184     *
185     * @return the monitored file
186     */
187    protected FileObject getFile()
188    {
189        try
190        {
191            FileSystemManager fsManager = VFS.getManager();
192            FileSystem fs = ((FileSystemBased) configuration).getFileSystem();
193            String uri = fs.getPath(null, configuration.getURL(), configuration.getBasePath(),
194                configuration.getFileName());
195            if (uri == null)
196            {
197                throw new ConfigurationRuntimeException("Unable to determine file to monitor");
198            }
199            return fsManager.resolveFile(uri);
200        }
201        catch (FileSystemException fse)
202        {
203            String msg = "Unable to monitor " + configuration.getURL().toString();
204            log.error(msg);
205            throw new ConfigurationRuntimeException(msg, fse);
206        }
207    }
208}