[FELIX-3398] Track new versions of artifact listener and optionally update all bundles when a new version is detected

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1310180 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 e1759c1..1e8fc51 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
@@ -101,6 +101,7 @@
     public final static String ENABLE_CONFIG_SAVE = "felix.fileinstall.enableConfigSave";
     public final static String START_LEVEL = "felix.fileinstall.start.level";
     public final static String ACTIVE_LEVEL = "felix.fileinstall.active.level";
+    public final static String UPDATE_WITH_LISTENERS = "felix.fileinstall.bundles.updateWithListeners";
 
     static final SecureRandom random = new SecureRandom();
 
@@ -120,6 +121,7 @@
     boolean noInitialDelay;
     int startLevel;
     int activeLevel;
+    boolean updateWithListeners;
 
     // Map of all installed artifacts
     Map/* <File, Artifact> */ currentManagedArtifacts = new HashMap/* <File, Artifact> */();
@@ -155,6 +157,7 @@
         noInitialDelay = getBoolean(properties, NO_INITIAL_DELAY, false);
         startLevel = getInt(properties, START_LEVEL, 0);    // by default, do not touch start level
         activeLevel = getInt(properties, ACTIVE_LEVEL, 0);    // by default, always scan
+        updateWithListeners = getBoolean(properties, UPDATE_WITH_LISTENERS, false); // Do not update bundles when listeners are updated
         this.context.addBundleListener(this);
 
         FilenameFilter flt;
@@ -338,8 +341,11 @@
         List/*<Artifact>*/ created = new ArrayList/*<Artifact>*/();
 
         // Try to process again files that could not be processed
-        files.addAll(processingFailures);
-        processingFailures.clear();
+        synchronized (processingFailures)
+        {
+            files.addAll(processingFailures);
+            processingFailures.clear();
+        }
 
         for (Iterator it = files.iterator(); it.hasNext(); )
         {
@@ -401,7 +407,10 @@
                         // processing for this artifact until one is found
                         if (listener == null)
                         {
-                            processingFailures.add(file);
+                            synchronized (processingFailures)
+                            {
+                                processingFailures.add(file);
+                            }
                             continue;
                         }
                         artifact.setListener(listener);
@@ -440,7 +449,10 @@
                     // processing for this artifact until one is found
                     if (listener == null)
                     {
-                        processingFailures.add(file);
+                        synchronized (processingFailures)
+                        {
+                            processingFailures.add(file);
+                        }
                         continue;
                     }
                     // Create the artifact
@@ -1355,4 +1367,50 @@
         return result;
     }
 
+    public void addListener(ArtifactListener listener, long stamp)
+    {
+        if (updateWithListeners)
+        {
+            for (Iterator it = currentManagedArtifacts.values().iterator(); it.hasNext(); )
+            {
+                Artifact artifact = (Artifact) it.next();
+                if (artifact.getListener() == null && artifact.getBundleId() > 0)
+                {
+                    Bundle bundle = context.getBundle(artifact.getBundleId());
+                    if (bundle != null && bundle.getLastModified() < stamp)
+                    {
+                        File path = artifact.getPath();
+                        if (listener.canHandle(path))
+                        {
+                            synchronized (processingFailures)
+                            {
+                                processingFailures.add(path);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        synchronized (this)
+        {
+            this.notifyAll();
+        }
+    }
+
+    public void removeListener(ArtifactListener listener)
+    {
+        for (Iterator it = currentManagedArtifacts.values().iterator(); it.hasNext(); )
+        {
+            Artifact artifact = (Artifact) it.next();
+            if (artifact.getListener() == listener)
+            {
+                artifact.setListener(null);
+            }
+        }
+        synchronized (this)
+        {
+            this.notifyAll();
+        }
+    }
+
 }
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 7e9ccbe..0099276 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
@@ -48,6 +48,7 @@
 import org.osgi.service.packageadmin.PackageAdmin;
 import org.osgi.service.startlevel.StartLevel;
 import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
 
 /**
  * This clever little bundle watches a directory and will install any jar file
@@ -55,7 +56,7 @@
  * fragment).
  *
  */
-public class FileInstall implements BundleActivator
+public class FileInstall implements BundleActivator, ServiceTrackerCustomizer
 {
     static ServiceTracker padmin;
     static ServiceTracker startLevel;
@@ -80,28 +81,11 @@
         padmin.open();
         startLevel = new ServiceTracker(context, StartLevel.class.getName(), null);
         startLevel.open();
+
         String flt = "(|(" + Constants.OBJECTCLASS + "=" + ArtifactInstaller.class.getName() + ")"
                      + "(" + Constants.OBJECTCLASS + "=" + ArtifactTransformer.class.getName() + ")"
                      + "(" + Constants.OBJECTCLASS + "=" + ArtifactUrlTransformer.class.getName() + "))";
-        listenersTracker = new ServiceTracker(context, FrameworkUtil.createFilter(flt), null)
-        {
-            public Object addingService(ServiceReference serviceReference)
-            {
-                ArtifactListener listener = (ArtifactListener) super.addingService(serviceReference);
-                addListener(serviceReference, listener);
-                return listener;
-            }
-            public void modifiedService(ServiceReference reference, Object service)
-            {
-                super.modifiedService(reference, service);
-                removeListener(reference);
-                addListener(reference, (ArtifactListener) service);
-            }
-            public void removedService(ServiceReference serviceReference, Object o)
-            {
-                removeListener(serviceReference);
-            }
-        };
+        listenersTracker = new ServiceTracker(context, FrameworkUtil.createFilter(flt), this);
         listenersTracker.open();
 
         try
@@ -157,6 +141,22 @@
         }
     }
 
+    public Object addingService(ServiceReference serviceReference)
+    {
+        ArtifactListener listener = (ArtifactListener) context.getService(serviceReference);
+        addListener(serviceReference, listener);
+        return listener;
+    }
+    public void modifiedService(ServiceReference reference, Object service)
+    {
+        removeListener(reference, (ArtifactListener) service);
+        addListener(reference, (ArtifactListener) service);
+    }
+    public void removedService(ServiceReference serviceReference, Object service)
+    {
+        removeListener(serviceReference, (ArtifactListener) service);
+    }
+
     // Adapted for FELIX-524
     private void set(Hashtable ht, String key)
     {
@@ -267,20 +267,9 @@
         {
             listeners.put(reference, listener);
         }
-        notifyWatchers();
-    }
+        
+        long currentStamp = reference.getBundle().getLastModified();
 
-    private void removeListener(ServiceReference reference)
-    {
-        synchronized (listeners)
-        {
-            listeners.remove(reference);
-        }
-        notifyWatchers();
-    }
-
-    private void notifyWatchers()
-    {
         List /*<DirectoryWatcher>*/ toNotify = new ArrayList /*<DirectoryWatcher>*/();
         synchronized (watchers)
         {
@@ -289,10 +278,25 @@
         for (Iterator w = toNotify.iterator(); w.hasNext();)
         {
             DirectoryWatcher dir = (DirectoryWatcher) w.next();
-            synchronized (dir)
-            {
-                dir.notifyAll();
-            }
+            dir.addListener( listener, currentStamp );
+        }
+    }
+
+    private void removeListener(ServiceReference reference, ArtifactListener listener)
+    {
+        synchronized (listeners)
+        {
+            listeners.remove(reference);
+        }
+        List /*<DirectoryWatcher>*/ toNotify = new ArrayList /*<DirectoryWatcher>*/();
+        synchronized (watchers)
+        {
+            toNotify.addAll(watchers.values());
+        }
+        for (Iterator w = toNotify.iterator(); w.hasNext();)
+        {
+            DirectoryWatcher dir = (DirectoryWatcher) w.next();
+            dir.removeListener( listener );
         }
     }