package com.asual.summer.core.resource.reload; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.core.io.Resource; import com.asual.summer.core.resource.PropertyResource; import com.asual.summer.core.util.ResourceUtils; public class FileChangedReloadingStrategy implements ReloadingStrategy { private final Log logger = LogFactory.getLog(getClass()); /** Constant for the jar URL protocol. */ private static final String JAR_PROTOCOL = "jar"; /** Constant for the default refresh delay. */ private static final int DEFAULT_REFRESH_DELAY = 5000; /** Stores a reference to the resource to be monitored. */ protected PropertyResource resource; /** The last time the configuration file was modified. */ protected long lastModified; /** The last time the file was checked for changes. */ protected long lastChecked = -1; /** The minimum delay in milliseconds between checks. */ protected long refreshDelay = DEFAULT_REFRESH_DELAY; /** A flag whether a reload is required. */ private boolean reloading = false; private File[] files; public void setPropertyResource(PropertyResource configuration) { this.resource = configuration; } public void init() { updateLastModified(); } public boolean reloadingRequired() { synchronized (this) { if (!reloading) { if(lastChecked > 0){ long now = System.currentTimeMillis(); if (now > lastChecked + refreshDelay) { lastChecked = now; if (hasChanged()) { reloading = true; } } } else{ lastChecked = System.currentTimeMillis(); } } return reloading; } } public void reloadingPerformed() { synchronized (this) { updateLastModified(); logger.info("Reloaded properties files."); } } /** * Return the minimal time in milliseconds between two reloadings. * * @return the refresh delay (in milliseconds) */ public long getRefreshDelay() { return refreshDelay; } /** * Set the minimal time between two reloadings. * * @param refreshDelay * refresh delay in milliseconds */ public void setRefreshDelay(long refreshDelay) { this.refreshDelay = refreshDelay; } /** * Update the last modified time. */ protected void updateLastModified() { for (File file : getFiles()) { if (file != null) { long fModified = file.lastModified(); if (fModified > lastModified) { lastModified = fModified; } } } reloading = false; } /** * Check if the configuration has changed since the last time it was loaded. * * @return a flag whether the configuration has changed */ protected boolean hasChanged() { boolean hasChanged = false; for (File file : getFiles()) { if (file == null || !file.exists()) { continue; } else if (file.lastModified() > lastModified) { hasChanged = true; break; } } return hasChanged; } /** * Returns the file that is monitored by this strategy. Note that the return * value can be <b>null </b> under some circumstances. * * @return the monitored file */ protected File[] getFiles() { if (files == null){ List<File> filesList = new ArrayList<File>(resource.getResources().length); for (int i = 0; i < resource.getResources().length; i++) { Resource location = resource.getResources()[i]; try { filesList.add(fileFromURL(location.getURL())); } catch (IOException e) { logger.warn("Missing file or invalid URL: " + location); } } files = filesList.toArray(new File[filesList.size()]); } return files; } /** * Helper method for transforming a URL into a file object. This method * handles file: and jar: URLs. * * @param url * the URL to be converted * @return the resulting file or <b>null </b> */ private File fileFromURL(URL url) { if (JAR_PROTOCOL.equals(url.getProtocol())) { String path = url.getPath(); try { return ResourceUtils.fileFromURL(new URL(path.substring(0, path.indexOf('!')))); } catch (MalformedURLException mex) { return null; } } else { return ResourceUtils.fileFromURL(url); } } }