FELIX-5137 Added felix.fileinstall.subdir.mode = jar | skip | recurse

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1719952 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/DirectoryWatcher.java b/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/DirectoryWatcher.java
index 4013dc0..f73b3ea 100644
--- a/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/DirectoryWatcher.java
+++ b/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/DirectoryWatcher.java
@@ -106,6 +106,7 @@
     public final static String OPTIONAL_SCOPE = "felix.fileinstall.optionalImportRefreshScope";
     public final static String FRAGMENT_SCOPE = "felix.fileinstall.fragmentRefreshScope";
     public final static String DISABLE_NIO2 = "felix.fileinstall.disableNio2";
+    public final static String SUBDIR_MODE = "felix.fileinstall.subdir.mode";
 
     public final static String SCOPE_NONE = "none";
     public final static String SCOPE_MANAGED = "managed";
@@ -185,12 +186,12 @@
         this.context.addBundleListener(this);
 
         if (disableNio2) {
-            scanner = new Scanner(watchedDirectory, filter);
+            scanner = new Scanner(watchedDirectory, filter, properties.get(SUBDIR_MODE));
         } else {
             try {
-                scanner = new WatcherScanner(context, watchedDirectory, filter);
+                scanner = new WatcherScanner(context, watchedDirectory, filter, properties.get(SUBDIR_MODE));
             } catch (Throwable t) {
-                scanner = new Scanner(watchedDirectory, filter);
+                scanner = new Scanner(watchedDirectory, filter, properties.get(SUBDIR_MODE));
             }
         }
     }
diff --git a/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/FileInstall.java b/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/FileInstall.java
index c94bde2..bd29152 100644
--- a/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/FileInstall.java
+++ b/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/FileInstall.java
@@ -104,6 +104,7 @@
             set(ht, DirectoryWatcher.NO_INITIAL_DELAY);
             set(ht, DirectoryWatcher.START_LEVEL);
             set(ht, DirectoryWatcher.OPTIONAL_SCOPE);
+            set(ht, DirectoryWatcher.SUBDIR_MODE);
 
             // check if dir is an array of dirs
             String dirs = ht.get(DirectoryWatcher.DIR);
diff --git a/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/Scanner.java b/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/Scanner.java
index 985c2ba..7fad43a 100644
--- a/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/Scanner.java
+++ b/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/Scanner.java
@@ -44,8 +44,15 @@
  */
 public class Scanner implements Closeable {
 
+    public final static String SUBDIR_MODE_JAR = "jar";
+    public final static String SUBDIR_MODE_SKIP = "skip";
+    public final static String SUBDIR_MODE_RECURSE = "recurse";
+
     final File directory;
     final FilenameFilter filter;
+    final boolean jarSubdir;
+    final boolean skipSubdir;
+    final boolean recurseSubdir;
 
     // Store checksums of files or directories
     Map<File, Long> lastChecksums = new HashMap<File, Long>();
@@ -58,7 +65,7 @@
      */
     public Scanner(File directory)
     {
-        this(directory, null);
+        this(directory, null, null);
     }
 
     /**
@@ -66,8 +73,9 @@
      *
      * @param directory the directory to scan
      * @param filterString a filter for file names
+     * @param subdirMode to use when scanning
      */
-    public Scanner(File directory, final String filterString)
+    public Scanner(File directory, final String filterString, String subdirMode)
     {
         this.directory = canon(directory);
         if (filterString != null && filterString.length() > 0)
@@ -85,6 +93,9 @@
         {
             this.filter = null;
         }
+        this.jarSubdir = subdirMode == null || SUBDIR_MODE_JAR.equals(subdirMode);
+        this.skipSubdir = SUBDIR_MODE_SKIP.equals(subdirMode);
+        this.recurseSubdir = SUBDIR_MODE_RECURSE.equals(subdirMode);
     }
 
     /**
@@ -117,25 +128,44 @@
         {
             return null;
         }
+        return processFiles(reportImmediately, list);
+    }
+
+    private Set<File> processFiles(boolean reportImmediately, File[] list)
+    {
         Set<File> files = new HashSet<File>();
         Set<File> removed = new HashSet<File>(storedChecksums.keySet());
         for (File file : list)
         {
+            if (file.isDirectory())
+            {
+                if (skipSubdir)
+                {
+                    continue;
+                } 
+                else if (recurseSubdir)
+                {
+                    files.addAll(processFiles(reportImmediately, file.listFiles(filter)));
+                    continue;
+                }
+            }
             long lastChecksum = lastChecksums.get(file) != null ? (Long) lastChecksums.get(file) : 0;
             long storedChecksum = storedChecksums.get(file) != null ? (Long) storedChecksums.get(file) : 0;
             long newChecksum = checksum(file);
             lastChecksums.put(file, newChecksum);
-            // Only handle file when it does not change anymore and it has changed since last reported
-            if ((newChecksum == lastChecksum || reportImmediately) && newChecksum != storedChecksum) {
+            // Only handle file when it does not change anymore and it has changed
+            // since last reported
+            if ((newChecksum == lastChecksum || reportImmediately) && newChecksum != storedChecksum)
+            {
                 storedChecksums.put(file, newChecksum);
                 files.add(file);
             }
             removed.remove(file);
         }
+        // Make sure we'll handle a file that has been deleted
+        files.addAll(removed);
         for (File file : removed)
         {
-            // Make sure we'll handle a file that has been deleted
-            files.addAll(removed);
             // Remove no longer used checksums
             lastChecksums.remove(file);
             storedChecksums.remove(file);
diff --git a/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/WatcherScanner.java b/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/WatcherScanner.java
index 11f02ed..532356f 100644
--- a/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/WatcherScanner.java
+++ b/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/WatcherScanner.java
@@ -42,9 +42,10 @@
      *
      * @param directory the directory to scan
      * @param filterString a filter for file names
+     * @param subdirMode to use when scanning
      */
-    public WatcherScanner(BundleContext bundleContext, File directory, String filterString) throws IOException {
-        super(directory, filterString);
+    public WatcherScanner(BundleContext bundleContext, File directory, String filterString, String subdirMode) throws IOException {
+        super(directory, filterString, subdirMode);
         this.bundleContext = bundleContext;
         if (filterString != null) {
             this.fileMatcher = FileSystems.getDefault().getPathMatcher("regex:" + filterString);
@@ -115,11 +116,23 @@
         @Override
         protected void process(Path path) {
             File file = path.toFile();
-            while (!file.getParentFile().equals(directory)) {
-                file = file.getParentFile();
-                if (file == null) {
+            if (!file.getParentFile().equals(directory)) {
+              // File is in a sub directory.
+              if (skipSubdir) {
+                return;
+              }
+              if (jarSubdir) {
+                // Walk up until the first level sub-directory.
+                do  {
+                  file = file.getParentFile();
+                  if (file == null) {
+                    // The file was not actually inside the watched directory.
+                    // Should not happen.
                     return;
-                }
+                  }
+                } while (!file.getParentFile().equals(directory));
+              }
+              // Otherwise we recurse by adding the file as-is.
             }
             synchronized (changed) {
                 changed.add(file);
@@ -128,16 +141,7 @@
 
         @Override
         protected void onRemove(Path path) {
-            File file = path.toFile();
-            while (!file.getParentFile().equals(directory)) {
-                file = file.getParentFile();
-                if (file == null) {
-                    return;
-                }
-            }
-            synchronized (changed) {
-                changed.add(file);
-            }
+            process(path);
         }
 
         @Override