summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPolina Genova2012-09-13 15:36:24 (EDT)
committer Borislav Kapukaranov2012-09-13 15:36:24 (EDT)
commit43e1a42138d6742aac8150d5a5c309fa12788753 (patch)
treea32cdd20010124f7ca0fc5d075ee007ac8bc0e24
parent5f1b78e2a5c77dd7487f481d7799ce8cb44f31c4 (diff)
downloadorg.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.
-rw-r--r--org.eclipse.virgo.util.io/src/main/java/org/eclipse/virgo/util/io/FileSystemChecker.java164
-rw-r--r--org.eclipse.virgo.util.io/src/main/java/org/eclipse/virgo/util/io/FileSystemListener.java12
-rw-r--r--org.eclipse.virgo.util.io/src/test/java/org/eclipse/virgo/util/io/FileSystemCheckerTests.java94
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");
+ }
+ }
}