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 java.io.File;
021    import java.net.MalformedURLException;
022    import java.net.URL;
023    
024    import org.apache.commons.configuration.ConfigurationUtils;
025    import org.apache.commons.configuration.FileConfiguration;
026    
027    /**
028     * <p>A reloading strategy that will reload the configuration every time its
029     * underlying file is changed.</p>
030     * <p>This reloading strategy does not actively monitor a configuration file,
031     * but is triggered by its associated configuration whenever properties are
032     * accessed. It then checks the configuration file's last modification date
033     * and causes a reload if this has changed.</p>
034     * <p>To avoid permanent disc access on successive property lookups a refresh
035     * delay can be specified. This has the effect that the configuration file's
036     * last modification date is only checked once in this delay period. The default
037     * value for this refresh delay is 5 seconds.</p>
038     * <p>This strategy only works with FileConfiguration instances.</p>
039     *
040     * @author Emmanuel Bourg
041     * @version $Revision: 606798 $, $Date: 2007-12-25 20:05:58 +0100 (Di, 25 Dez 2007) $
042     * @since 1.1
043     */
044    public class FileChangedReloadingStrategy implements ReloadingStrategy
045    {
046        /** Constant for the jar URL protocol.*/
047        private static final String JAR_PROTOCOL = "jar";
048    
049        /** Constant for the default refresh delay.*/
050        private static final int DEFAULT_REFRESH_DELAY = 5000;
051    
052        /** Stores a reference to the configuration to be monitored.*/
053        protected FileConfiguration configuration;
054    
055        /** The last time the configuration file was modified. */
056        protected long lastModified;
057    
058        /** The last time the file was checked for changes. */
059        protected long lastChecked;
060    
061        /** The minimum delay in milliseconds between checks. */
062        protected long refreshDelay = DEFAULT_REFRESH_DELAY;
063    
064        /** A flag whether a reload is required.*/
065        private boolean reloading;
066    
067        public void setConfiguration(FileConfiguration configuration)
068        {
069            this.configuration = configuration;
070        }
071    
072        public void init()
073        {
074            updateLastModified();
075        }
076    
077        public boolean reloadingRequired()
078        {
079            if (!reloading)
080            {
081                long now = System.currentTimeMillis();
082    
083                if (now > lastChecked + refreshDelay)
084                {
085                    lastChecked = now;
086                    if (hasChanged())
087                    {
088                        reloading = true;
089                    }
090                }
091            }
092    
093            return reloading;
094        }
095    
096        public void reloadingPerformed()
097        {
098            updateLastModified();
099        }
100    
101        /**
102         * Return the minimal time in milliseconds between two reloadings.
103         *
104         * @return the refresh delay (in milliseconds)
105         */
106        public long getRefreshDelay()
107        {
108            return refreshDelay;
109        }
110    
111        /**
112         * Set the minimal time between two reloadings.
113         *
114         * @param refreshDelay refresh delay in milliseconds
115         */
116        public void setRefreshDelay(long refreshDelay)
117        {
118            this.refreshDelay = refreshDelay;
119        }
120    
121        /**
122         * Update the last modified time.
123         */
124        protected void updateLastModified()
125        {
126            File file = getFile();
127            if (file != null)
128            {
129                lastModified = file.lastModified();
130            }
131            reloading = false;
132        }
133    
134        /**
135         * Check if the configuration has changed since the last time it was loaded.
136         *
137         * @return a flag whether the configuration has changed
138         */
139        protected boolean hasChanged()
140        {
141            File file = getFile();
142            if (file == null || !file.exists())
143            {
144                return false;
145            }
146    
147            return file.lastModified() > lastModified;
148        }
149    
150        /**
151         * Returns the file that is monitored by this strategy. Note that the return
152         * value can be <b>null </b> under some circumstances.
153         *
154         * @return the monitored file
155         */
156        protected File getFile()
157        {
158            return (configuration.getURL() != null) ? fileFromURL(configuration
159                    .getURL()) : configuration.getFile();
160        }
161    
162        /**
163         * Helper method for transforming a URL into a file object. This method
164         * handles file: and jar: URLs.
165         *
166         * @param url the URL to be converted
167         * @return the resulting file or <b>null </b>
168         */
169        private File fileFromURL(URL url)
170        {
171            if (JAR_PROTOCOL.equals(url.getProtocol()))
172            {
173                String path = url.getPath();
174                try
175                {
176                    return ConfigurationUtils.fileFromURL(new URL(path.substring(0,
177                            path.indexOf('!'))));
178                }
179                catch (MalformedURLException mex)
180                {
181                    return null;
182                }
183            }
184            else
185            {
186                return ConfigurationUtils.fileFromURL(url);
187            }
188        }
189    }