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
This commit is contained in:
parent
548f6bac5a
commit
1bd0adabfb
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -36,6 +36,7 @@ public class PolicyMonitorTest
|
|||
count.incrementAndGet();
|
||||
}
|
||||
};
|
||||
monitor.setScanInterval(1);
|
||||
|
||||
monitor.start();
|
||||
|
||||
|
|
|
@ -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<Listener> _listeners = new ArrayList<Listener>();
|
||||
private final Map<String,Long> _prevScan = new HashMap<String,Long> ();
|
||||
private final Map<String,Long> _currentScan = new HashMap<String,Long> ();
|
||||
private final Map<String,TimeNSize> _prevScan = new HashMap<String,TimeNSize> ();
|
||||
private final Map<String,TimeNSize> _currentScan = new HashMap<String,TimeNSize> ();
|
||||
private FilenameFilter _filter;
|
||||
private final List<File> _scanDirs = new ArrayList<File>();
|
||||
private volatile boolean _running = false;
|
||||
|
@ -57,6 +57,43 @@ public class Scanner
|
|||
private TimerTask _task;
|
||||
private int _scanDepth=0;
|
||||
|
||||
public enum Notification { ADDED, CHANGED, REMOVED };
|
||||
private final Map<String,Notification> _notifications = new HashMap<String,Notification>();
|
||||
|
||||
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<String,Long> currentScan, Map<String,Long> oldScan)
|
||||
public synchronized void reportDifferences (Map<String,TimeNSize> currentScan, Map<String,TimeNSize> oldScan)
|
||||
{
|
||||
List<String> bulkChanges = new ArrayList<String>();
|
||||
// scan the differences and add what was found to the map of notifications:
|
||||
|
||||
Set<String> oldScanKeys = new HashSet<String>(oldScan.keySet());
|
||||
Iterator<Entry<String, Long>> itor = currentScan.entrySet().iterator();
|
||||
while (itor.hasNext())
|
||||
{
|
||||
Map.Entry<String, Long> entry = itor.next();
|
||||
if (!oldScanKeys.contains(entry.getKey()))
|
||||
{
|
||||
Log.debug("File added: "+entry.getKey());
|
||||
reportAddition ((String)entry.getKey());
|
||||
bulkChanges.add(entry.getKey());
|
||||
}
|
||||
else if (!oldScan.get(entry.getKey()).equals(entry.getValue()))
|
||||
{
|
||||
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())
|
||||
// Look for new and changed files
|
||||
for (Map.Entry<String, TimeNSize> entry: currentScan.entrySet())
|
||||
{
|
||||
|
||||
Iterator<String> keyItor = oldScanKeys.iterator();
|
||||
while (keyItor.hasNext())
|
||||
String file = entry.getKey();
|
||||
if (!oldScanKeys.contains(file))
|
||||
{
|
||||
String filename = (String)keyItor.next();
|
||||
Log.debug("File removed: "+filename);
|
||||
reportRemoval(filename);
|
||||
bulkChanges.add(filename);
|
||||
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(file).equals(currentScan.get(file)))
|
||||
{
|
||||
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<String> bulkChanges = new ArrayList<String>();
|
||||
for (Iterator<Entry<String,Notification>> iter = _notifications.entrySet().iterator();iter.hasNext();)
|
||||
{
|
||||
Entry<String,Notification> 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<String,Long> scanInfoMap, int depth)
|
||||
private void scanFile (File f, Map<String,TimeNSize> 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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue