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    }