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 018package org.apache.commons.configuration.reloading; 019 020import java.io.File; 021import java.net.MalformedURLException; 022import java.net.URL; 023 024import org.apache.commons.configuration.ConfigurationUtils; 025import org.apache.commons.configuration.FileConfiguration; 026import org.apache.commons.logging.Log; 027import org.apache.commons.logging.LogFactory; 028 029/** 030 * <p>A reloading strategy that will reload the configuration every time its 031 * underlying file is changed.</p> 032 * <p>This reloading strategy does not actively monitor a configuration file, 033 * but is triggered by its associated configuration whenever properties are 034 * accessed. It then checks the configuration file's last modification date 035 * and causes a reload if this has changed.</p> 036 * <p>To avoid permanent disc access on successive property lookups a refresh 037 * delay can be specified. This has the effect that the configuration file's 038 * last modification date is only checked once in this delay period. The default 039 * value for this refresh delay is 5 seconds.</p> 040 * <p>This strategy only works with FileConfiguration instances.</p> 041 * 042 * @author Emmanuel Bourg 043 * @version $Id: FileChangedReloadingStrategy.java 1210646 2011-12-05 21:25:01Z oheger $ 044 * @since 1.1 045 */ 046public class FileChangedReloadingStrategy implements ReloadingStrategy 047{ 048 /** Constant for the jar URL protocol.*/ 049 private static final String JAR_PROTOCOL = "jar"; 050 051 /** Constant for the default refresh delay.*/ 052 private static final int DEFAULT_REFRESH_DELAY = 5000; 053 054 /** Stores a reference to the configuration to be monitored.*/ 055 protected FileConfiguration configuration; 056 057 /** The last time the configuration file was modified. */ 058 protected long lastModified; 059 060 /** The last time the file was checked for changes. */ 061 protected long lastChecked; 062 063 /** The minimum delay in milliseconds between checks. */ 064 protected long refreshDelay = DEFAULT_REFRESH_DELAY; 065 066 /** A flag whether a reload is required.*/ 067 private boolean reloading; 068 069 /** The Log to use for diagnostic messages */ 070 private Log logger = LogFactory.getLog(FileChangedReloadingStrategy.class); 071 072 public void setConfiguration(FileConfiguration configuration) 073 { 074 this.configuration = configuration; 075 } 076 077 public void init() 078 { 079 updateLastModified(); 080 } 081 082 public boolean reloadingRequired() 083 { 084 if (!reloading) 085 { 086 long now = System.currentTimeMillis(); 087 088 if (now > lastChecked + refreshDelay) 089 { 090 lastChecked = now; 091 if (hasChanged()) 092 { 093 if (logger.isDebugEnabled()) 094 { 095 logger.debug("File change detected: " + getName()); 096 } 097 reloading = true; 098 } 099 } 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}