FELIX-4223 document service event tracking, start code cleanup on way to avoiding collisions between config updates and activation/deactivation

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1523551 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/manager/AbstractComponentManager.java b/scr/src/main/java/org/apache/felix/scr/impl/manager/AbstractComponentManager.java
index 4b81918..d95b721 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/manager/AbstractComponentManager.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/manager/AbstractComponentManager.java
@@ -264,6 +264,11 @@
         }
     }
 
+    /**
+     * We effectively maintain the set of completely processed service event tracking counts.  This method waits for all events prior 
+     * to the parameter tracking count to complete, then returns.  See further documentation in EdgeInfo.
+     * @param trackingCount
+     */
     void waitForTracked( int trackingCount )
     {
         m_missingLock.lock();
@@ -785,7 +790,6 @@
             disposeInternal( reason );
             return;
         }
-        m_activated = false;
         log( LogService.LOG_DEBUG, "Deactivating component", null );
 
         // catch any problems from deleting the component to prevent the
@@ -821,7 +825,6 @@
         log( LogService.LOG_DEBUG, "Disposing component (reason: " + reason + ")", null );
         if ( m_activated )
         {
-            m_activated = false;
             doDeactivate( reason, true );
         }
         clear();
@@ -840,6 +843,7 @@
             obtainWriteLock( "AbstractComponentManager.State.doDeactivate.1" );
             try
             {
+                m_activated = false;
                 deleteComponent( reason );
                 deactivateDependencyManagers();
                 if ( disable )
@@ -1090,7 +1094,6 @@
         if ( m_activator != null )
         {
             m_activator.unregisterComponentId( this );
-//            m_activator = null;
         }
     }
 
@@ -1270,9 +1273,10 @@
     private void disableDependencyManagers()
     {
         log( LogService.LOG_DEBUG, "Disabling dependency managers", null);
+        AtomicInteger trackingCount = new AtomicInteger();
         for ( DependencyManager<S, ?> dm: getDependencyManagers() )
         {
-            dm.unregisterServiceListener();
+            dm.unregisterServiceListener( trackingCount );
         }
     }
 
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/manager/DependencyManager.java b/scr/src/main/java/org/apache/felix/scr/impl/manager/DependencyManager.java
index 8a72c2e..4c701cf 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/manager/DependencyManager.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/manager/DependencyManager.java
@@ -1680,6 +1680,7 @@
         // null. This is valid for both immediate and delayed components
         if ( componentInstance != null )
         {
+            //TODO needs sync on getTracker().tracked()
             if (info.outOfRange( trackingCount ) )
             {
                 //wait for unbinds to complete
@@ -1885,7 +1886,13 @@
         }
 
         final ServiceTracker<T, RefPair<T>> oldTracker = m_tracker;
-        SortedMap<ServiceReference<T>, RefPair<T>> refMap = unregisterServiceListener();
+        AtomicInteger trackingCount = new AtomicInteger();
+        SortedMap<ServiceReference<T>, RefPair<T>> refMap = unregisterServiceListener( trackingCount );
+        if ( trackingCount.get() != -1 )
+        {
+            //wait for service events to complete before processing initial set from new tracker.
+            m_componentManager.waitForTracked( trackingCount.get() );
+        }
         m_componentManager.log( LogService.LOG_DEBUG, "Setting target property for dependency {0} to {1}", new Object[]
                 {getName(), target}, null );
         BundleContext bundleContext = m_componentManager.getBundleContext();
@@ -1972,13 +1979,12 @@
         return customizer;
     }
 
-    SortedMap<ServiceReference<T>, RefPair<T>> unregisterServiceListener()
+    SortedMap<ServiceReference<T>, RefPair<T>> unregisterServiceListener( AtomicInteger trackingCount )
     {
         SortedMap<ServiceReference<T>, RefPair<T>> refMap;
         ServiceTracker<T, RefPair<T>> tracker = m_tracker;
         if ( tracker != null )
         {
-            AtomicInteger trackingCount = new AtomicInteger( );
             refMap = tracker.close( trackingCount );
             m_tracker = null;
             m_componentManager.log( LogService.LOG_DEBUG, "unregistering service listener for dependency {0}", new Object[]
@@ -1989,6 +1995,7 @@
             refMap = new TreeMap<ServiceReference<T>, RefPair<T>>(Collections.reverseOrder());
             m_componentManager.log( LogService.LOG_DEBUG, " No existing service listener to unregister for dependency {0}", new Object[]
                     {getName()}, null );
+            trackingCount.set( -1 );
         }
 //        m_registered = false;
         return refMap;
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/manager/EdgeInfo.java b/scr/src/main/java/org/apache/felix/scr/impl/manager/EdgeInfo.java
index 57aef9f..dfd9098 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/manager/EdgeInfo.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/manager/EdgeInfo.java
@@ -20,6 +20,29 @@
 
 import java.util.concurrent.CountDownLatch;
 
+/**
+ * EdgeInfo holds information about the service event tracking counts for creating (open) and disposing (close) 
+ * implementation object instances per dependency manager.  These need to be maintained for each implementation object instance
+ * because each instance (for a service factory) will have different sets of service references available.  These need to be 
+ * maintained for each dependency manager because the open/close tracking counts are obtained when the set of current
+ * service references is obtained, using a lock internal to the service tracker.
+ * 
+ *
+ * The information in the open/close counts is used in the outOfRange method which determines if a service event tracking count 
+ * occurred before the "open" event (in which case it is reflected in the open set already and does not need to be processed separately)
+ * or after the "close" event (in which case it is reflected in the close set already).
+ * 
+ * The open latch is used to make sure that elements in the open set are completely processed before updated or unbind events
+ *  are processed
+ * The close latch is used to make sure that unbind events that are out of range wait for the close to complete before returning; 
+ * in this case the unbind is happening in the "close" thread rather than the service event thread, so we wait for the close to complete 
+ * so that when the service event returns the unbind will actually have been completed.
+ * 
+ * Related to this functionality is the missing tracking in AbstractComponentManager.  This is used on close of an instance to make 
+ * sure all service events occuring before close starts complete processing before the close takes action.
+ *
+ */
+
 class EdgeInfo
 {
     private int open = -1;
@@ -41,6 +64,7 @@
     {
         this.openLatch = latch;
     }
+    
     public CountDownLatch getCloseLatch()
     {
         return closeLatch;
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/manager/ImmediateComponentManager.java b/scr/src/main/java/org/apache/felix/scr/impl/manager/ImmediateComponentManager.java
index 4d4bfb9..fc872dd 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/manager/ImmediateComponentManager.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/manager/ImmediateComponentManager.java
@@ -56,7 +56,7 @@
     private volatile ComponentContextImpl<S> m_componentContext;
 
     // the component holder responsible for managing this component
-    private ComponentHolder m_componentHolder;
+    private final ComponentHolder m_componentHolder;
 
     // optional properties provided in the ComponentFactory.newInstance method
     private Dictionary<String, Object> m_factoryProperties;
@@ -167,15 +167,19 @@
         if ( m_componentContext != null )
         {
             m_useCount.set( 0 );
-            m_componentContext.setImplementationAccessible( false );
             disposeImplementationObject( m_componentContext, reason );
             m_componentContext = null;
             log( LogService.LOG_DEBUG, "Unset and deconfigured implementation object for component {0} in deleteComponent for reason {1}", new Object[] { getName(), REASONS[ reason ] },  null );
-            m_properties = null;
-            m_serviceProperties = null;
+            clearServiceProperties();
         }
     }
 
+    void clearServiceProperties()
+    {
+        m_properties = null;
+        m_serviceProperties = null;
+    }
+
 
     public ComponentInstance getComponentInstance()
     {
@@ -324,6 +328,7 @@
     protected void disposeImplementationObject( ComponentContextImpl<S> componentContext,
             int reason )
     {
+        componentContext.setImplementationAccessible( false );
         S implementationObject = componentContext.getImplementationObject( false );
 
         // 1. Call the deactivate method, if present
@@ -566,6 +571,9 @@
             // clear the current properties to force using the configuration data
             m_properties = null;
 
+            
+            //TODO wait for activation/deactivation to complete, then lock(?) or internal disable...
+            
             // unsatisfied component and non-ignored configuration may change targets
             // to satisfy references
             if ( getState() == STATE_UNSATISFIED
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/manager/ServiceFactoryComponentManager.java b/scr/src/main/java/org/apache/felix/scr/impl/manager/ServiceFactoryComponentManager.java
index 8653616..da43d9c 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/manager/ServiceFactoryComponentManager.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/manager/ServiceFactoryComponentManager.java
@@ -88,6 +88,7 @@
             log( LogService.LOG_DEBUG, "Unset implementation object for component {0} in deleteComponent for reason {1}", new Object[] { getName(), REASONS[ reason ] },  null );
         }
         serviceContexts.clear();
+        clearServiceProperties();
     }