Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
FileChangedReloadingStrategy |
|
| 2.4166666666666665;2,417 |
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 | 96 | 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 | 96 | 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 | 96 | private Log logger = LogFactory.getLog(FileChangedReloadingStrategy.class); |
71 | ||
72 | public void setConfiguration(FileConfiguration configuration) | |
73 | { | |
74 | 97 | this.configuration = configuration; |
75 | 97 | } |
76 | ||
77 | public void init() | |
78 | { | |
79 | 85 | updateLastModified(); |
80 | 85 | } |
81 | ||
82 | public boolean reloadingRequired() | |
83 | { | |
84 | 77364 | if (!reloading) |
85 | { | |
86 | 77363 | long now = System.currentTimeMillis(); |
87 | ||
88 | 77363 | if (now > lastChecked + refreshDelay) |
89 | { | |
90 | 70 | lastChecked = now; |
91 | 70 | if (hasChanged()) |
92 | { | |
93 | 10 | if (logger.isDebugEnabled()) |
94 | { | |
95 | 0 | logger.debug("File change detected: " + getName()); |
96 | } | |
97 | 10 | reloading = true; |
98 | } | |
99 | } | |
100 | } | |
101 | ||
102 | 77364 | return reloading; |
103 | } | |
104 | ||
105 | public void reloadingPerformed() | |
106 | { | |
107 | 14129 | updateLastModified(); |
108 | 14129 | } |
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 | 18 | 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 | 67 | this.refreshDelay = refreshDelay; |
128 | 67 | } |
129 | ||
130 | /** | |
131 | * Update the last modified time. | |
132 | */ | |
133 | protected void updateLastModified() | |
134 | { | |
135 | 14214 | File file = getFile(); |
136 | 14214 | if (file != null) |
137 | { | |
138 | 14213 | lastModified = file.lastModified(); |
139 | } | |
140 | 14214 | reloading = false; |
141 | 14214 | } |
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 | 69 | File file = getFile(); |
151 | 69 | if (file == null || !file.exists()) |
152 | { | |
153 | 5 | if (logger.isWarnEnabled() && lastModified != 0) |
154 | { | |
155 | 1 | logger.warn("File was deleted: " + getName(file)); |
156 | 1 | lastModified = 0; |
157 | } | |
158 | 5 | return false; |
159 | } | |
160 | ||
161 | 64 | 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 | 14286 | 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 | 14277 | if (JAR_PROTOCOL.equals(url.getProtocol())) |
186 | { | |
187 | 2 | String path = url.getPath(); |
188 | try | |
189 | { | |
190 | 2 | return ConfigurationUtils.fileFromURL(new URL(path.substring(0, |
191 | path.indexOf('!')))); | |
192 | } | |
193 | 0 | catch (MalformedURLException mex) |
194 | { | |
195 | 0 | return null; |
196 | } | |
197 | } | |
198 | else | |
199 | { | |
200 | 14275 | return ConfigurationUtils.fileFromURL(url); |
201 | } | |
202 | } | |
203 | ||
204 | private String getName() | |
205 | { | |
206 | 0 | return getName(getFile()); |
207 | } | |
208 | ||
209 | private String getName(File file) | |
210 | { | |
211 | 1 | String name = configuration.getURL().toString(); |
212 | 1 | if (name == null) |
213 | { | |
214 | 0 | if (file != null) |
215 | { | |
216 | 0 | name = file.getAbsolutePath(); |
217 | } | |
218 | else | |
219 | { | |
220 | 0 | name = "base: " + configuration.getBasePath() |
221 | + "file: " + configuration.getFileName(); | |
222 | } | |
223 | } | |
224 | 1 | return name; |
225 | } | |
226 | } |