View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.configuration.reloading;
19  
20  import java.io.File;
21  import java.net.MalformedURLException;
22  import java.net.URL;
23  
24  import org.apache.commons.configuration.ConfigurationUtils;
25  import org.apache.commons.configuration.FileConfiguration;
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  
29  /**
30   * <p>A reloading strategy that will reload the configuration every time its
31   * underlying file is changed.</p>
32   * <p>This reloading strategy does not actively monitor a configuration file,
33   * but is triggered by its associated configuration whenever properties are
34   * accessed. It then checks the configuration file's last modification date
35   * and causes a reload if this has changed.</p>
36   * <p>To avoid permanent disc access on successive property lookups a refresh
37   * delay can be specified. This has the effect that the configuration file's
38   * last modification date is only checked once in this delay period. The default
39   * value for this refresh delay is 5 seconds.</p>
40   * <p>This strategy only works with FileConfiguration instances.</p>
41   *
42   * @author Emmanuel Bourg
43   * @version $Id: FileChangedReloadingStrategy.java 1210646 2011-12-05 21:25:01Z oheger $
44   * @since 1.1
45   */
46  public class FileChangedReloadingStrategy implements ReloadingStrategy
47  {
48      /** Constant for the jar URL protocol.*/
49      private static final String JAR_PROTOCOL = "jar";
50  
51      /** Constant for the default refresh delay.*/
52      private static final int DEFAULT_REFRESH_DELAY = 5000;
53  
54      /** Stores a reference to the configuration to be monitored.*/
55      protected FileConfiguration configuration;
56  
57      /** The last time the configuration file was modified. */
58      protected long lastModified;
59  
60      /** The last time the file was checked for changes. */
61      protected long lastChecked;
62  
63      /** The minimum delay in milliseconds between checks. */
64      protected long refreshDelay = DEFAULT_REFRESH_DELAY;
65  
66      /** A flag whether a reload is required.*/
67      private boolean reloading;
68  
69      /** The Log to use for diagnostic messages */
70      private Log logger = LogFactory.getLog(FileChangedReloadingStrategy.class);
71  
72      public void setConfiguration(FileConfiguration configuration)
73      {
74          this.configuration = configuration;
75      }
76  
77      public void init()
78      {
79          updateLastModified();
80      }
81  
82      public boolean reloadingRequired()
83      {
84          if (!reloading)
85          {
86              long now = System.currentTimeMillis();
87  
88              if (now > lastChecked + refreshDelay)
89              {
90                  lastChecked = now;
91                  if (hasChanged())
92                  {
93                      if (logger.isDebugEnabled())
94                      {
95                          logger.debug("File change detected: " + getName());
96                      }
97                      reloading = true;
98                  }
99              }
100         }
101 
102         return reloading;
103     }
104 
105     public void reloadingPerformed()
106     {
107         updateLastModified();
108     }
109 
110     /**
111      * Return the minimal time in milliseconds between two reloadings.
112      *
113      * @return the refresh delay (in milliseconds)
114      */
115     public long getRefreshDelay()
116     {
117         return refreshDelay;
118     }
119 
120     /**
121      * Set the minimal time between two reloadings.
122      *
123      * @param refreshDelay refresh delay in milliseconds
124      */
125     public void setRefreshDelay(long refreshDelay)
126     {
127         this.refreshDelay = refreshDelay;
128     }
129 
130     /**
131      * Update the last modified time.
132      */
133     protected void updateLastModified()
134     {
135         File file = getFile();
136         if (file != null)
137         {
138             lastModified = file.lastModified();
139         }
140         reloading = false;
141     }
142 
143     /**
144      * Check if the configuration has changed since the last time it was loaded.
145      *
146      * @return a flag whether the configuration has changed
147      */
148     protected boolean hasChanged()
149     {
150         File file = getFile();
151         if (file == null || !file.exists())
152         {
153             if (logger.isWarnEnabled() && lastModified != 0)
154             {
155                 logger.warn("File was deleted: " + getName(file));
156                 lastModified = 0;
157             }
158             return false;
159         }
160 
161         return file.lastModified() > lastModified;
162     }
163 
164     /**
165      * Returns the file that is monitored by this strategy. Note that the return
166      * value can be <b>null </b> under some circumstances.
167      *
168      * @return the monitored file
169      */
170     protected File getFile()
171     {
172         return (configuration.getURL() != null) ? fileFromURL(configuration
173                 .getURL()) : configuration.getFile();
174     }
175 
176     /**
177      * Helper method for transforming a URL into a file object. This method
178      * handles file: and jar: URLs.
179      *
180      * @param url the URL to be converted
181      * @return the resulting file or <b>null </b>
182      */
183     private File fileFromURL(URL url)
184     {
185         if (JAR_PROTOCOL.equals(url.getProtocol()))
186         {
187             String path = url.getPath();
188             try
189             {
190                 return ConfigurationUtils.fileFromURL(new URL(path.substring(0,
191                         path.indexOf('!'))));
192             }
193             catch (MalformedURLException mex)
194             {
195                 return null;
196             }
197         }
198         else
199         {
200             return ConfigurationUtils.fileFromURL(url);
201         }
202     }
203 
204     private String getName()
205     {
206         return getName(getFile());
207     }
208 
209     private String getName(File file)
210     {
211         String name = configuration.getURL().toString();
212         if (name == null)
213         {
214             if (file != null)
215             {
216                 name = file.getAbsolutePath();
217             }
218             else
219             {
220                 name = "base: " + configuration.getBasePath()
221                        + "file: " + configuration.getFileName();
222             }
223         }
224         return name;
225     }
226 }