From: <bra...@us...> - 2011-11-16 22:10:44
|
Revision: 3558 http://archive-access.svn.sourceforge.net/archive-access/?rev=3558&view=rev Author: bradtofel Date: 2011-11-16 22:10:38 +0000 (Wed, 16 Nov 2011) Log Message: ----------- FEATURE: Now optionally allows configuration of a set of Spring files to be monitored by a background Thread, which will re-load and swap a new configuration into place if one of the files in the monitored set changes. Modified Paths: -------------- trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/RequestFilter.java Modified: trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/RequestFilter.java =================================================================== --- trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/RequestFilter.java 2011-11-16 22:09:06 UTC (rev 3557) +++ trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/RequestFilter.java 2011-11-16 22:10:38 UTC (rev 3558) @@ -23,6 +23,8 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import java.util.logging.LogManager; import java.util.logging.Logger; @@ -36,6 +38,8 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.archive.wayback.util.MonitoredFileSet; + /** * Top-Level integration point between a series of RequestHandler mappings and a * generic ServletContext. This filter is assumed to be responsible for matching @@ -46,19 +50,25 @@ * @author brad */ public class RequestFilter implements Filter { - private static final Logger LOGGER = Logger.getLogger(RequestFilter.class - .getName()); - private RequestMapper mapper = null; + private static final Logger LOGGER = + Logger.getLogger(RequestFilter.class.getName()); + private final static String CONFIG_PATH = "config-path"; private final static String LOGGING_CONFIG_PATH = "logging-config-path"; + private final static String MONITOR_MS_CONFIG = "monitor-ms"; + private final static String MONITOR_FILES_CONFIG = "monitor-files"; + private UpdateThread thread = null; + private RequestMapper mapper = null; + private ServletContext context; + private String springConfigPath; + public void init(FilterConfig config) throws ServletException { - ServletContext servletContext = config.getServletContext(); + context = config.getServletContext(); - String logConfigPath = servletContext - .getInitParameter(LOGGING_CONFIG_PATH); + String logConfigPath = context.getInitParameter(LOGGING_CONFIG_PATH); if (logConfigPath != null) { - String resolvedLogPath = servletContext.getRealPath(logConfigPath); + String resolvedLogPath = context.getRealPath(logConfigPath); File logConfigFile = new File(resolvedLogPath); if (logConfigFile.exists()) { FileInputStream finp = null; @@ -83,21 +93,46 @@ } } - String configPath = servletContext.getInitParameter(CONFIG_PATH); + String configPath = context.getInitParameter(CONFIG_PATH); if (configPath == null) { throw new ServletException("Missing " + CONFIG_PATH + " parameter"); } - String resolvedPath = servletContext.getRealPath(configPath); + springConfigPath = context.getRealPath(configPath); - LOGGER.info("Initializing Spring config at: " + resolvedPath); - mapper = SpringReader.readSpringConfig(resolvedPath, servletContext); - LOGGER.info("Initialized Spring config at: " + resolvedPath); - } + String monitorFiles = context.getInitParameter(MONITOR_FILES_CONFIG); + if(monitorFiles == null) { + // just load once: + mapper = loadRequestMapper(); + } else { - public void destroy() { - LOGGER.info("Shutdown starting."); - mapper.shutdown(); - LOGGER.info("Shutdown complete."); + // we're in fancy mode: start the background thread to watch + // our Spring config - it will swap out our mapper when things + // change + + String monitorMSString = context.getInitParameter(MONITOR_MS_CONFIG); + long monitorMS = 10000; + if(monitorMSString != null) { + try { + monitorMS = Long.parseLong(monitorMSString); + } catch(NumberFormatException e) { + throw new ServletException("Non int for " + MONITOR_MS_CONFIG); + } + } + String[] monitored = monitorFiles.split(","); + + ArrayList<String> monitoredL = new ArrayList<String>(); + for(String monitoredPath : monitored) { + monitoredL.add(monitoredPath); + } + thread = new UpdateThread(this, monitorMS, monitoredL); + + // TODO: should we force initial load of a mapper? + // it means incoming requests will block until we're ready.. + // if we don't the thread will immediately being loading + // the Spring config, and will swap it in when it's ready + thread.reloadMapper(); + thread.start(); + } } public void doFilter(ServletRequest request, ServletResponse response, @@ -107,8 +142,12 @@ try { if (request instanceof HttpServletRequest) { if (response instanceof HttpServletResponse) { - handled = mapper.handleRequest((HttpServletRequest) request, - (HttpServletResponse) response); + if(mapper != null) { + handled = mapper.handleRequest( + (HttpServletRequest) request, + (HttpServletResponse) response); + + } } } } finally { @@ -118,4 +157,112 @@ chain.doFilter(request, response); } } + + public void destroy() { + LOGGER.info("Shutdown starting."); + if(thread != null) { + thread.interrupt(); + } + if(mapper != null) { + mapper.shutdown(); + } + LOGGER.info("Shutdown complete."); + } + + + private RequestMapper loadRequestMapper() { + LOGGER.info("Initializing Spring config at: " + springConfigPath); + RequestMapper newMapper = SpringReader.readSpringConfig(springConfigPath, context); + LOGGER.info("Initialized Spring config at: " + springConfigPath); + return newMapper; + } + + /** + * @return the mapper + */ + public RequestMapper getMapper() { + return mapper; + } + + /** + * @param mapper the mapper to set + */ + public void setMapper(RequestMapper mapper) { + this.mapper = mapper; + } + + /** + * Thread that repeatedly checks a set of Spring config files. If any + * change, then a new RequestMapper is created from them, which is then + * swapped in on the containing RequestFilter. The old one if present is + * shut down. + * + * @author Brad Tofel + */ + private class UpdateThread extends Thread { + /** + * object which merges CDX files with the BDBResourceIndex + */ + private RequestFilter filter = null; + private long runInterval; + + private MonitoredFileSet fileSet; + private MonitoredFileSet.FileState activeState; + + /** + * @param filter the RequestFilter we will update + * @param runInterval number of MS bewtween checks + * @param monitored List of files to check Mod Time to trigger reload + */ + public UpdateThread(RequestFilter filter, + long runInterval, List<String> monitored) { + + super("RequestFilter.UpdateThread"); + super.setDaemon(true); + this.filter = filter; + this.runInterval = runInterval; + + fileSet = new MonitoredFileSet(monitored); + activeState = null; + } + + public void reloadMapper() { + + MonitoredFileSet.FileState startState = fileSet.getFileState(); + + RequestMapper mapper = filter.loadRequestMapper(); + + if(fileSet.isChanged(startState)) { + // erk.. files changed during the operation.. update nothing.. + LOGGER.warning("Files changed during Spring reload... discarding.."); + mapper.shutdown(); + + } else { + LOGGER.warning("Loaded RequestMapper."); + RequestMapper oldMapper = filter.getMapper(); + filter.setMapper(mapper); + if(oldMapper != null) { + // shut it down (cross fingers first) + LOGGER.warning("Shutting Down old RequestMapper."); + oldMapper.shutdown(); + } + activeState = startState; + } + } + + public void run() { + LOGGER.info("RequestFilter.UpdateThread is alive."); + while (true) { + try { + if((activeState == null) || fileSet.isChanged(activeState)) { + reloadMapper(); + } + sleep(runInterval); + } catch (InterruptedException e) { + LOGGER.info("Shutting Down."); + return; + } + } + } + } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |