Java tutorial
/* * Copyright 2007-2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package org.impalaframework.module.monitor; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.impalaframework.file.FileMonitor; import org.impalaframework.file.monitor.FileMonitorImpl; import org.impalaframework.file.monitor.MonitorFileFilter; import org.impalaframework.util.ArrayUtils; import org.impalaframework.util.ResourceUtils; import org.springframework.core.io.Resource; /** * {@link ModuleChangeMonitor} which scans for changes in modules and automatically * reloads modules when detecting changes. * * @author Phil Zoio */ public class ScheduledModuleChangeMonitor implements ModuleChangeMonitor { private static final Log logger = LogFactory.getLog(ScheduledModuleChangeMonitor.class); private static final int DEFAULT_INITIAL_DELAY_SECONDS = 10; private static final int DEFAULT_INTERVAL_SECONDS = 2; private int initialDelay = DEFAULT_INTERVAL_SECONDS; private int checkInterval = DEFAULT_INITIAL_DELAY_SECONDS; private String[] autoReloadFileIncludes; private String[] autoReloadFileExcludes; private FileMonitor fileMonitor; private ScheduledExecutorService executor; public ScheduledModuleChangeMonitor() { super(); } private List<ModuleContentChangeListener> modificationListeners = new ArrayList<ModuleContentChangeListener>(); private Map<String, ResourceInfo> resourcesToMonitor = new ConcurrentHashMap<String, ResourceInfo>(); /* ************************* ModuleChangeMonitor methods ************************** */ /** * Adds {@link ModuleContentChangeListener} */ public void addModificationListener(ModuleContentChangeListener listener) { modificationListeners.add(listener); } /** * Wires in the {@link Resource} instances which should be monitored on behalf of individual modules */ public void setResourcesToMonitor(String moduleName, Resource[] resources) { if (!ArrayUtils.isNullOrEmpty(resources)) { logger.info("Monitoring for changes in module " + moduleName + ": " + Arrays.toString(resources)); resourcesToMonitor.put(moduleName, new ResourceInfo(System.currentTimeMillis(), resources)); } else { logger.info("No resources to monitor for module " + moduleName); } } /** * Starts the {@link ScheduledExecutorService}, settings off a task which periodically * checks the monitorable resources and potentially initiates a module reload when necessary, by sending a * {@link ModuleChangeEvent} to registered {@link ModuleContentChangeListener}s. */ public void start() { logger.info("Starting " + this.getClass().getName() + " with fixed delay of " + initialDelay + " and interval of " + checkInterval); setDefaultsIfNecessary(); executor.scheduleWithFixedDelay(new Runnable() { public void run() { try { if (!checkForChanges()) { return; } List<ModuleChangeInfo> modified = new LinkedList<ModuleChangeInfo>(); final Set<String> moduleNames = resourcesToMonitor.keySet(); for (String moduleName : moduleNames) { ResourceInfo ri = resourcesToMonitor.get(moduleName); if (ri != null) { // should be null except for case where item is // removed File[] files = ResourceUtils.getFiles(ri.resources); //get the last modified files long lastModified = fileMonitor.lastModified(files); if (lastModified > ri.lastModified) { // add to the list of modified modules modified.add(new ModuleChangeInfo(moduleName)); // set the ResourceInfo object ri.lastModified = lastModified; } } } if (!modified.isEmpty()) { logger.info("Found modified modules: " + modified); final ModuleChangeEvent event = new ModuleChangeEvent(modified); for (ModuleContentChangeListener listener : modificationListeners) { listener.moduleContentsModified(event); } } else { if (logger.isDebugEnabled()) logger.debug("Completed check for modified modules. No modified module contents found"); } } catch (Exception e) { logger.error("Error invoking module content change listeners: " + e.getMessage(), e); } } }, initialDelay, checkInterval, TimeUnit.SECONDS); } public void stop() { executor.shutdown(); } /* ************************* Helper methods ************************** */ void setDefaultsIfNecessary() { if (fileMonitor == null) { FileMonitorImpl fileMonitor = new FileMonitorImpl(); final List<String> includes = autoReloadFileIncludes != null ? Arrays.asList(autoReloadFileIncludes) : new ArrayList<String>(); final List<String> excludes = autoReloadFileExcludes != null ? Arrays.asList(autoReloadFileExcludes) : new ArrayList<String>(); fileMonitor.setFileFilter(new MonitorFileFilter(includes, excludes)); this.fileMonitor = fileMonitor; } if (executor == null) executor = Executors.newSingleThreadScheduledExecutor(); } /* ************************* Protected methods ************************** */ /** * Simply returns true. More sophisticated implementations can implement other strategies for determining whether to * initiate a module reload, such as by inspecting a touch file. */ protected boolean checkForChanges() { return true; } /* ************************* Dependency injection setters ************************** */ public void setModificationListeners(List<ModuleContentChangeListener> modificationListeners) { this.modificationListeners.clear(); this.modificationListeners.addAll(modificationListeners); } public void setInitialDelay(int initialDelay) { this.initialDelay = initialDelay; } public void setCheckInterval(int interval) { this.checkInterval = interval; } public void setAutoReloadFileIncludes(String[] autoReloadIncludes) { this.autoReloadFileIncludes = autoReloadIncludes; } public void setAutoReloadFileExcludes(String[] autoReloadExcludes) { this.autoReloadFileExcludes = autoReloadExcludes; } /* ************************* ResourceInfo private class ************************** */ private class ResourceInfo { private long lastModified; private Resource[] resources; public ResourceInfo(long lastModified, Resource[] resources) { super(); this.lastModified = lastModified; this.resources = resources; } } }