| author | Polina Genova | 2012-09-13 15:36:24 (EDT) |
|---|---|---|
| committer | Borislav Kapukaranov | 2012-09-13 15:36:24 (EDT) |
| commit | 43e1a42138d6742aac8150d5a5c309fa12788753 (patch) (side-by-side diff) | |
| tree | a32cdd20010124f7ca0fc5d075ee007ac8bc0e24 | |
| parent | 5f1b78e2a5c77dd7487f481d7799ce8cb44f31c4 (diff) | |
| download | org.eclipse.virgo.util-43e1a42138d6742aac8150d5a5c309fa12788753.zip org.eclipse.virgo.util-43e1a42138d6742aac8150d5a5c309fa12788753.tar.gz org.eclipse.virgo.util-43e1a42138d6742aac8150d5a5c309fa12788753.tar.bz2 | |
Bug 389418 - [util] Create a new InitialEvent that works with List of artefacts.
3 files changed, 246 insertions, 24 deletions
diff --git a/org.eclipse.virgo.util.io/src/main/java/org/eclipse/virgo/util/io/FileSystemChecker.java b/org.eclipse.virgo.util.io/src/main/java/org/eclipse/virgo/util/io/FileSystemChecker.java index d51c35b..7f2a173 100644 --- a/org.eclipse.virgo.util.io/src/main/java/org/eclipse/virgo/util/io/FileSystemChecker.java +++ b/org.eclipse.virgo.util.io/src/main/java/org/eclipse/virgo/util/io/FileSystemChecker.java @@ -13,12 +13,15 @@ package org.eclipse.virgo.util.io; import java.io.File; import java.io.FilenameFilter; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Pattern; import org.eclipse.virgo.util.common.Assert; @@ -47,6 +50,15 @@ public final class FileSystemChecker { private final Logger logger; private final Object checkLock = new Object(); + + + /** + * Enables bulk handling of all initially observed file system objects (so that they are handled altogether at once + * and not one by one) + */ + private final String INITIAL_EVENT_HANDLING_MODE = "org.eclipse.virgo.fschecker.initialEventMode"; + private final String BULK_MODE_VALUE = "bulk"; + private AtomicBoolean isInitialEventsHandlingInitiatedOnce = new AtomicBoolean(false); /** * The files we know about -- with their last modified date (as a Long) are in <code>fileState</code>.<br/> @@ -63,7 +75,7 @@ public final class FileSystemChecker { private final List<FileSystemListener> listeners = new CopyOnWriteArrayList<FileSystemListener>(); private final FilenameFilter includeFilter; - + private static boolean WINDOWS = System.getProperty("os.name").startsWith("Windows"); /** @@ -128,30 +140,133 @@ public final class FileSystemChecker { public void addListener(FileSystemListener listener) { this.listeners.add(listener); } + + /** + * Returns a list of all the files that are recorded as INITIAL events. + * + * @param files + * - the array of files that is checked + */ + private List<File> getInitialFiles(File[] files) { + List<File> resultFiles = new ArrayList<File>(); + for (File file : files) { + String keyFilePath = this.key(file); + if (monitorRecords.containsKey(keyFilePath)) { + MonitorRecord monitorRecord = monitorRecords.get(keyFilePath); + if (FileSystemEvent.INITIAL.equals(monitorRecord.getEvent())) { + resultFiles.add(file); + } + } + } + return resultFiles; + } + + /** + * Bulk handling of initial files (not one by one). + * + * @param initialFiles + */ + private void handleInitialFiles(List<File> initialFiles) { + notifyListenersOnInitialEvent(initialFiles); + for (File file : initialFiles) { + monitorRecords.remove(this.key(file)); + setKnownFileState(file); + } + } + + /** + * Returns the absolute file paths of the given files + * + * @param files + * @return + */ + private List<String> getPaths(List<File> files) { + List<String> filePaths = new ArrayList<String>(); + for (File file : files) { + filePaths.add(this.key(file)); + } + return filePaths; + } + + /** + * Notify once all registered listeners for the INITIAL event + * + * @param initialFiles + */ + private void notifyListenersOnInitialEvent(List<File> initialFiles) { + List<String> initialFilesPaths = getPaths(initialFiles); + for (FileSystemListener listener : this.listeners) { + try { + listener.onInitialEvent(initialFilesPaths); + } catch (Throwable e) { + if (logger != null) { + logger.warn("Listener threw exception for event " + + FileSystemEvent.INITIAL, e); + } + } + } + } + + /** + * Removes a given list of files from a given array. + * + * @param inputArray + * @param filesToRemove + * @return + */ + private File[] removeFilesFromArray(File[] inputArray, + List<File> filesToRemove) { + List<File> reducedFiles = new ArrayList<File>(Arrays.asList(inputArray)); + reducedFiles.removeAll(filesToRemove); + return reducedFiles.toArray(new File[reducedFiles.size()]); + } + + private void addToCurrentFileKeys(Set<String> currentFileKeys, + List<File> files) { + for (File file : files) { + currentFileKeys.add(this.key(file)); + } + } + + /** + * Instructs this <code>FileSystemChecker</code> to check the configured + * directory and notifies any registered listeners of changes to the + * directory files. + */ + public void check() { + synchronized (this.checkLock) { + try { + File[] currentFiles; + try { + currentFiles = listCurrentDirFiles(); + } catch (Exception e) { + if (logger != null) + logger.warn("FileSystemChecker caught exception from listFiles()", e); + throw e; + } + + debugState("before check:", currentFiles); + + Set<String> currentFileKeys = new HashSet<String>( + currentFiles.length); + + if (isInitialEventsBulkHandlingEnabled()) { + // optimize handling of initial events - do it only once + if (isInitialEventsHandlingInitiatedOnce.compareAndSet(false, true)) { + List<File> initialFiles = getInitialFiles(currentFiles); + if (!initialFiles.isEmpty()) { + handleInitialFiles(initialFiles); + addToCurrentFileKeys(currentFileKeys, initialFiles); + // skip further processing of initialFiles in the current check + currentFiles = removeFilesFromArray(currentFiles, initialFiles); + } + } + } - /** - * Instructs this <code>FileSystemChecker</code> to check the configured directory and notifies any registered - * listeners of changes to the directory files. - */ - public void check() { - synchronized (this.checkLock) { - try { - File[] currentFiles; - try { - currentFiles = listCurrentDirFiles(); - } catch (Exception e) { - if (logger!=null) logger.warn("FileSystemChecker caught exception from listFiles()", e); - throw e; - } - - debugState("before check:", currentFiles); - - Set<String> currentFileKeys = new HashSet<String>(currentFiles.length); for (File file : currentFiles) { // remember seen files to allow comparison for delete String keyFile = this.key(file); currentFileKeys.add(keyFile); - if (!isKnown(file)) { // not seen it before -- start monitoring it -- a potential newly created file monitorRecords.put(keyFile, new MonitorRecord(file.length(), FileSystemEvent.CREATED)); @@ -203,7 +318,7 @@ public final class FileSystemChecker { } } } - + public boolean isUnlocked(File file) { // Heuristic check for the file not being locked on Windows. On *ix, assume the file is unlocked since we can't tell. return !WINDOWS || file.renameTo(file); @@ -353,7 +468,10 @@ public final class FileSystemChecker { public FileSystemEvent getEvent() { return event; - } - + } } + + private boolean isInitialEventsBulkHandlingEnabled(){ + return BULK_MODE_VALUE.equalsIgnoreCase(System.getProperty(INITIAL_EVENT_HANDLING_MODE)); + } } diff --git a/org.eclipse.virgo.util.io/src/main/java/org/eclipse/virgo/util/io/FileSystemListener.java b/org.eclipse.virgo.util.io/src/main/java/org/eclipse/virgo/util/io/FileSystemListener.java index 3b13b61..1707355 100644 --- a/org.eclipse.virgo.util.io/src/main/java/org/eclipse/virgo/util/io/FileSystemListener.java +++ b/org.eclipse.virgo.util.io/src/main/java/org/eclipse/virgo/util/io/FileSystemListener.java @@ -11,6 +11,8 @@ package org.eclipse.virgo.util.io; +import java.util.List; + /** * Listener that is notified of file system modifications.<p/> * @@ -28,4 +30,14 @@ public interface FileSystemListener { * @param event the event that occurred. */ void onChange(String path, FileSystemEvent event); + + + /** + * Signals once for all initially observed file system objects. + * The method is convenient when all initial file system events + * need to be handled all together, not one by one. + * @param paths all the file paths for which INITIAL event occurred + */ + void onInitialEvent(List<String> paths); + } diff --git a/org.eclipse.virgo.util.io/src/test/java/org/eclipse/virgo/util/io/FileSystemCheckerTests.java b/org.eclipse.virgo.util.io/src/test/java/org/eclipse/virgo/util/io/FileSystemCheckerTests.java index ce21678..8ca1326 100644 --- a/org.eclipse.virgo.util.io/src/test/java/org/eclipse/virgo/util/io/FileSystemCheckerTests.java +++ b/org.eclipse.virgo.util.io/src/test/java/org/eclipse/virgo/util/io/FileSystemCheckerTests.java @@ -25,10 +25,12 @@ import org.eclipse.virgo.util.io.FileSystemEvent; import org.eclipse.virgo.util.io.FileSystemListener; import org.eclipse.virgo.util.io.FileSystemUtils; import org.eclipse.virgo.util.io.StubLogger.StubLogEntry; +import org.eclipse.virgo.util.io.StubLogger; import org.junit.After; import org.junit.Before; import org.junit.Test; + /** */ public class FileSystemCheckerTests { @@ -64,6 +66,10 @@ public class FileSystemCheckerTests { eventReceived.set(true); } } + + public void onInitialEvent(List<String> paths){ + } + }); File newFile = new File(this.checkDir, fileName); newFile.createNewFile(); @@ -91,6 +97,10 @@ public class FileSystemCheckerTests { eventReceived.set(true); } } + + public void onInitialEvent(List<String> paths){ + } + }); File newFile = new File(this.checkDir, fileName); newFile.createNewFile(); @@ -118,6 +128,9 @@ public class FileSystemCheckerTests { eventReceived.set(true); } } + + public void onInitialEvent(List<String> paths){ + } }); File newFile = new File(this.checkDir, fileName); newFile.createNewFile(); @@ -145,6 +158,9 @@ public class FileSystemCheckerTests { eventReceived.set(true); } } + + public void onInitialEvent(List<String> paths){ + } }); File updateFile = new File(this.checkDir, fileName); updateFile.createNewFile(); @@ -214,6 +230,9 @@ public class FileSystemCheckerTests { } } } + + public void onInitialEvent(List<String> paths){ + } public boolean checkEvents(int all, int ini, int cre, int del, int mod) { return all == this.eA.get() && ini == this.eI.get() && cre == this.eC.get() && del == this.eD.get() && mod == this.eM.get(); @@ -298,14 +317,19 @@ public class FileSystemCheckerTests { initialEvents.incrementAndGet(); } } + + public void onInitialEvent(List<String> paths){ + } }); checker.check(); assertEquals("Expected 2 INITIAL events", 2, initialEvents.get()); checker.check(); assertEquals("Too many INITIAL events", 2, initialEvents.get()); - } @Test + } + + @Test public void initialStateDebug() throws Exception { StubLogger stubLogger = new StubLogger(); new File(this.checkDir, "a.txt").createNewFile(); @@ -319,6 +343,9 @@ public class FileSystemCheckerTests { initialEvents.incrementAndGet(); } } + + public void onInitialEvent(List<String> paths){ + } }); checker.check(); assertEquals("Expected 2 INITIAL events", 2, initialEvents.get()); @@ -346,4 +373,69 @@ public class FileSystemCheckerTests { // System.out.println(sle); // } } + + @Test + public void onInitialEventTest() throws Exception { + try{ + //enables onInitialFSChanges notifications + System.setProperty("org.eclipse.virgo.fschecker.initialEventMode", "bulk"); + File f1 = new File(this.checkDir, "a.txt"); + File f2 = new File(this.checkDir, "b.txt"); + f1.createNewFile(); + f2.createNewFile(); + final AtomicInteger onChangeEventsCounter = new AtomicInteger(0); + final AtomicInteger onInitialEventsCounter = new AtomicInteger(0); + final AtomicBoolean eventFilesCheckFlag = new AtomicBoolean(true); + FileSystemChecker checker = new FileSystemChecker(this.checkDir); + checker.addListener(new FileSystemListener() { + + public void onChange(String file, FileSystemEvent event) { + onChangeEventsCounter.incrementAndGet(); + } + + public void onInitialEvent(List<String> paths){ + onInitialEventsCounter.incrementAndGet(); + if (paths.size()== 2){ + for (String s:paths){ + if (!(s.endsWith("a.txt") || s.endsWith("b.txt"))){ + eventFilesCheckFlag.set(false); + } + } + } else eventFilesCheckFlag.set(false); + } + + }); + checker.check(); + assertEquals("Expected only 1 onInitialEvent event for the 2 files:", 1, onInitialEventsCounter.get()); + assertTrue("Expected onInitialFSChanges event for 2 files - a.txt and b.txt:", eventFilesCheckFlag.get()); + assertEquals("Expected no onChange events:", 0, onChangeEventsCounter.get()); + onInitialEventsCounter.set(0); + onChangeEventsCounter.set(0); + + f1.setLastModified(System.currentTimeMillis()); + f2.setLastModified(System.currentTimeMillis()); + checker.check();//here only marked for monitoring + checker.check();//onChange events + assertEquals("Expected no new onInitialEvent events:", 0, onInitialEventsCounter.get()); + assertEquals("Expected 2 onChange event for the 2 updated files:", 2, onChangeEventsCounter.get()); + onInitialEventsCounter.set(0); + onChangeEventsCounter.set(0); + + new File(this.checkDir, "c.txt").createNewFile(); + checker.check();//here only marked for monitoring + checker.check();//onChange events + assertEquals("Expected no new onInitialEvent events:", 0, onInitialEventsCounter.get()); + assertEquals("Expected 1 onChange event for the new file:", 1, onChangeEventsCounter.get()); + onInitialEventsCounter.set(0); + onChangeEventsCounter.set(0); + + createDir(); //clear dir + checker.check(); + assertEquals("Expected no new onInitialEvent events:", 0, onInitialEventsCounter.get()); + assertEquals("Expected 3 onChange events for the 3 deleted files:", 3, onChangeEventsCounter.get()); + + }finally { + System.setProperty("org.eclipse.virgo.fschecker.initialEventMode", "singular"); + } + } } |

