    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
    018    package org.apache.commons.configuration.reloading;
    019
    020    import org.apache.commons.configuration.ConfigurationRuntimeException;
    021    import org.apache.commons.configuration.FileConfiguration;
    022    import org.apache.commons.configuration.FileSystem;
    023    import org.apache.commons.configuration.FileSystemBased;
    024    import org.apache.commons.logging.Log;
    025    import org.apache.commons.logging.LogFactory;
    026    import org.apache.commons.vfs2.FileObject;
    027    import org.apache.commons.vfs2.FileSystemException;
    028    import org.apache.commons.vfs2.FileSystemManager;
    029    import 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.java 1162383 2011-08-27 15:57:11Z oheger $
    050     * @since 1.7
    051     */
    052    public 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    }