From 1bd0adabfb01a3def0303d86668d07fd5f11623f Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Fri, 1 Apr 2011 02:18:18 +0000 Subject: [PATCH] 340949 Scanner delays file notifications until files are stable git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@2948 7e9141cc-0065-0410-87d8-b60c137991c4 --- VERSION.txt | 1 + .../org/eclipse/jetty/client/ExpireTest.java | 2 +- .../deploy/providers/WebAppProviderTest.java | 4 +- .../eclipse/jetty/policy/PolicyMonitor.java | 6 +- .../jetty/policy/PolicyMonitorTest.java | 1 + .../java/org/eclipse/jetty/util/Scanner.java | 171 +++++++++++++----- 6 files changed, 139 insertions(+), 46 deletions(-) diff --git a/VERSION.txt b/VERSION.txt index bde2b549038..e6629b0fc78 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -10,6 +10,7 @@ jetty-7.3.2-SNAPSHOT + 340838 Update ConnectHandler to perform half closes properly + 340878 Integrations should be able to load their own keystores + 340920 Dynamically assign RMI registry port for integration testing + + 340949 Scanner delays file notifications until files are stable + 341006 Move inner enums out into separate file + 341105 Stack trace is printed for an ignored exception + 341145 WebAppContext MBean attribute serverClasses returns empty value diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/ExpireTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/ExpireTest.java index fee9e6fe888..0b645d6fd5a 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/ExpireTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/ExpireTest.java @@ -16,6 +16,7 @@ package org.eclipse.jetty.client; import static org.junit.Assert.assertTrue; import java.io.IOException; +import java.io.InterruptedIOException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -60,7 +61,6 @@ public class ExpireTest } catch (InterruptedException x) { - throw new ServletException(x); } } }); diff --git a/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/WebAppProviderTest.java b/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/WebAppProviderTest.java index 0f2e562f15b..8e6357bd437 100644 --- a/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/WebAppProviderTest.java +++ b/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/WebAppProviderTest.java @@ -13,8 +13,8 @@ import org.junit.Test; public class WebAppProviderTest { - @Rule - public TestingDir testdir = new TestingDir(); + @Rule + public TestingDir testdir = new TestingDir(); private static XmlConfiguredJetty jetty; @Before diff --git a/jetty-policy/src/main/java/org/eclipse/jetty/policy/PolicyMonitor.java b/jetty-policy/src/main/java/org/eclipse/jetty/policy/PolicyMonitor.java index 93cf4b7182d..64a1755fecf 100644 --- a/jetty-policy/src/main/java/org/eclipse/jetty/policy/PolicyMonitor.java +++ b/jetty-policy/src/main/java/org/eclipse/jetty/policy/PolicyMonitor.java @@ -40,7 +40,7 @@ public abstract class PolicyMonitor extends AbstractLifeCycle /** * scan interval in seconds for policy file changes */ - private int _scanInterval = 10; + private int _scanInterval = 1; /** * specialized listener enabling waitForScan() functionality @@ -72,6 +72,7 @@ public abstract class PolicyMonitor extends AbstractLifeCycle */ public PolicyMonitor( String directory ) { + this(); _policyDirectory = directory; } @@ -144,7 +145,8 @@ public abstract class PolicyMonitor extends AbstractLifeCycle */ public synchronized void waitForScan() throws Exception { - CountDownLatch latch = new CountDownLatch(1); + // wait for 2 scans for stable files + CountDownLatch latch = new CountDownLatch(2); _scanningListener.setScanningLatch(latch); _scanner.scan(); diff --git a/jetty-policy/src/test/java/org/eclipse/jetty/policy/PolicyMonitorTest.java b/jetty-policy/src/test/java/org/eclipse/jetty/policy/PolicyMonitorTest.java index 0c16fe5c65a..8174d61c06d 100644 --- a/jetty-policy/src/test/java/org/eclipse/jetty/policy/PolicyMonitorTest.java +++ b/jetty-policy/src/test/java/org/eclipse/jetty/policy/PolicyMonitorTest.java @@ -36,6 +36,7 @@ public class PolicyMonitorTest count.incrementAndGet(); } }; + monitor.setScanInterval(1); monitor.start(); diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/Scanner.java b/jetty-util/src/main/java/org/eclipse/jetty/util/Scanner.java index 9456299f4e6..08e030815bb 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/Scanner.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/Scanner.java @@ -29,6 +29,7 @@ import java.util.Set; import java.util.Timer; import java.util.TimerTask; +import org.eclipse.jetty.util.component.AbstractLifeCycle; import org.eclipse.jetty.util.log.Log; @@ -38,16 +39,15 @@ import org.eclipse.jetty.util.log.Log; * Utility for scanning a directory for added, removed and changed * files and reporting these events via registered Listeners. * - * TODO AbstractLifeCycle */ -public class Scanner +public class Scanner extends AbstractLifeCycle { private static int __scannerId=0; private int _scanInterval; private int _scanCount = 0; private final List _listeners = new ArrayList(); - private final Map _prevScan = new HashMap (); - private final Map _currentScan = new HashMap (); + private final Map _prevScan = new HashMap (); + private final Map _currentScan = new HashMap (); private FilenameFilter _filter; private final List _scanDirs = new ArrayList(); private volatile boolean _running = false; @@ -56,8 +56,45 @@ public class Scanner private Timer _timer; private TimerTask _task; private int _scanDepth=0; + + public enum Notification { ADDED, CHANGED, REMOVED }; + private final Map _notifications = new HashMap(); - + static class TimeNSize + { + final long _lastModified; + final long _size; + + public TimeNSize(long lastModified, long size) + { + _lastModified = lastModified; + _size = size; + } + + @Override + public int hashCode() + { + return (int)_lastModified^(int)_size; + } + + @Override + public boolean equals(Object o) + { + if (o instanceof TimeNSize) + { + TimeNSize tns = (TimeNSize)o; + return tns._lastModified==_lastModified && tns._size==_size; + } + return false; + } + + @Override + public String toString() + { + return "[lm="+_lastModified+",s="+_size+"]"; + } + } + /** * Listener * @@ -274,7 +311,7 @@ public class Scanner /** * Start the scanning action. */ - public synchronized void start () + public synchronized void doStart() { if (_running) return; @@ -285,6 +322,7 @@ public class Scanner { // if files exist at startup, report them scan(); + scan(); // scan twice so files reported as stable } else { @@ -321,14 +359,14 @@ public class Scanner { _timer = newTimer(); _task = newTimerTask(); - _timer.schedule(_task, 1000L*getScanInterval(),1000L*getScanInterval()); + _timer.schedule(_task, 1010L*getScanInterval(),1010L*getScanInterval()); } } } /** * Stop the scanning. */ - public synchronized void stop () + public synchronized void doStop() { if (_running) { @@ -388,45 +426,97 @@ public class Scanner * @param currentScan the info from the most recent pass * @param oldScan info from the previous pass */ - public void reportDifferences (Map currentScan, Map oldScan) + public synchronized void reportDifferences (Map currentScan, Map oldScan) { - List bulkChanges = new ArrayList(); - + // scan the differences and add what was found to the map of notifications: + Set oldScanKeys = new HashSet(oldScan.keySet()); - Iterator> itor = currentScan.entrySet().iterator(); - while (itor.hasNext()) + + // Look for new and changed files + for (Map.Entry entry: currentScan.entrySet()) { - Map.Entry entry = itor.next(); - if (!oldScanKeys.contains(entry.getKey())) + String file = entry.getKey(); + if (!oldScanKeys.contains(file)) { - Log.debug("File added: "+entry.getKey()); - reportAddition ((String)entry.getKey()); - bulkChanges.add(entry.getKey()); + Notification old=_notifications.put(file,Notification.ADDED); + if (old!=null) + { + switch(old) + { + case REMOVED: + case CHANGED: + _notifications.put(file,Notification.CHANGED); + } + } } - else if (!oldScan.get(entry.getKey()).equals(entry.getValue())) + else if (!oldScan.get(file).equals(currentScan.get(file))) { - Log.debug("File changed: "+entry.getKey()); - reportChange((String)entry.getKey()); - oldScanKeys.remove(entry.getKey()); - bulkChanges.add(entry.getKey()); - } - else - oldScanKeys.remove(entry.getKey()); - } - - if (!oldScanKeys.isEmpty()) - { - - Iterator keyItor = oldScanKeys.iterator(); - while (keyItor.hasNext()) - { - String filename = (String)keyItor.next(); - Log.debug("File removed: "+filename); - reportRemoval(filename); - bulkChanges.add(filename); + Notification old=_notifications.put(file,Notification.CHANGED); + if (old!=null) + { + switch(old) + { + case ADDED: + _notifications.put(file,Notification.ADDED); + } + } } } + // Look for deleted files + for (String file : oldScan.keySet()) + { + if (!currentScan.containsKey(file)) + { + Notification old=_notifications.put(file,Notification.REMOVED); + if (old!=null) + { + switch(old) + { + case ADDED: + _notifications.remove(file); + } + } + } + } + + if (Log.isDebugEnabled()) + Log.debug("scanned "+_notifications); + + // Process notifications + // Only process notifications that are for stable files (ie same in old and current scan). + List bulkChanges = new ArrayList(); + for (Iterator> iter = _notifications.entrySet().iterator();iter.hasNext();) + { + Entry entry=iter.next(); + String file=entry.getKey(); + + // Is the file stable? + if (oldScan.containsKey(file)) + { + if (!oldScan.get(file).equals(currentScan.get(file))) + continue; + } + else if (currentScan.containsKey(file)) + continue; + + // File is stable so notify + Notification notification=entry.getValue(); + iter.remove(); + bulkChanges.add(file); + switch(notification) + { + case ADDED: + reportAddition(file); + break; + case CHANGED: + reportChange(file); + break; + case REMOVED: + reportRemoval(file); + break; + } + } if (!bulkChanges.isEmpty()) reportBulkChanges(bulkChanges); } @@ -438,7 +528,7 @@ public class Scanner * @param f file or directory * @param scanInfoMap map of filenames to last modified times */ - private void scanFile (File f, Map scanInfoMap, int depth) + private void scanFile (File f, Map scanInfoMap, int depth) { try { @@ -450,8 +540,7 @@ public class Scanner if ((_filter == null) || ((_filter != null) && _filter.accept(f.getParentFile(), f.getName()))) { String name = f.getCanonicalPath(); - long lastModified = f.lastModified(); - scanInfoMap.put(name, new Long(lastModified)); + scanInfoMap.put(name, new TimeNSize(f.lastModified(),f.length())); } }