FELIX-3754 Add temporary implementation object provisioning to support
  binding services during calls to the activate method but preventing
  access to the implementation object before the activate method has
  successfully terminated.

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1410211 13f79535-47bb-0310-9956-ffa450edef68
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 e4647da..efce051 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
@@ -52,6 +52,11 @@
     // The object that implements the service and that is bound to other services
     private volatile Object m_implementationObject;
 
+    // The component implementation object temporarily set to allow
+    // for service updates during activation. This field is only set
+    // to a non-null value while calling the activate method
+    private volatile Object m_tmpImplementationObject;
+
     // keep the using bundles as reference "counters" for instance deactivation
     private volatile int m_useCount;
 
@@ -122,15 +127,20 @@
                 {
                     m_componentContext = tmpContext;
                     m_implementationObject = implementationObject;
+                    m_tmpImplementationObject = null;
                 }
 
 
-                public void unsetImplementationObject( Object implementationObject )
+                public void presetImplementationObject( Object implementationObject )
                 {
-                    m_componentContext = null;
-                    m_implementationObject = null;
+                    m_tmpImplementationObject = implementationObject;
                 }
 
+
+                public void resetImplementationObject( Object implementationObject )
+                {
+                    m_tmpImplementationObject = null;
+                }
             } );
 
             // if something failed creating the component instance, return false
@@ -186,11 +196,44 @@
         return m_implementationObject;
     }
 
-    protected interface SetImplementationObject {
-        void setImplementationObject(Object implementationObject);
-        void unsetImplementationObject(Object implementationObject);
+    /**
+     * The <code>SetImplementationObject</code> interface provides an
+     * API for component managers to setup the implementation object and
+     * potentially other parts as part of the {@link #createImplementationObject} method
+     * processing.
+     */
+    protected interface SetImplementationObject
+    {
+
+        /**
+         * Presets the implementation object. This method is called before
+         * the component's activator method is called and is intended to
+         * temporarily set the implementation object during the activator
+         * call.
+         */
+        void presetImplementationObject( Object implementationObject );
+
+
+        /**
+         * Resets the implementation object. This method is called after
+         * the activator method terminates with an error and is intended to
+         * revert any temporary settings done in the {@link #presetImplementationObject(Object)}
+         * method.
+         */
+        void resetImplementationObject( Object implementationObject );
+
+
+        /**
+         * Sets the implementation object. This method is called after
+         * the activator methid terminates successfully and is intended to
+         * complete setting the implementation object. Temporary presets done
+         * by the {@link #presetImplementationObject(Object)} should be
+         * removed and the implementation object is now accessible.
+         */
+        void setImplementationObject( Object implementationObject );
     }
 
+
     protected Object createImplementationObject( ComponentContext componentContext, SetImplementationObject setter )
     {
         final Class implementationObjectClass;
@@ -245,7 +288,7 @@
         }
 
         // 4. set the implementation object prematurely
-        setter.setImplementationObject( implementationObject );
+        setter.presetImplementationObject( implementationObject );
 
         // 5. Call the activate method, if present
         final MethodResult result = getComponentMethods().getActivateMethod().invoke( implementationObject, new ActivatorParameter(
@@ -253,7 +296,7 @@
         if ( result == null )
         {
             // make sure the implementation object is not available
-            setter.unsetImplementationObject( implementationObject );
+            setter.resetImplementationObject( implementationObject );
 
             // 112.5.8 If the activate method throws an exception, SCR must log an error message
             // containing the exception with the Log Service and activation fails
@@ -268,6 +311,7 @@
         }
         else
         {
+            setter.setImplementationObject( implementationObject );
             setServiceProperties( result );
         }
 
@@ -314,17 +358,20 @@
 
     void update( DependencyManager dependencyManager, ServiceReference ref )
     {
-        dependencyManager.update( m_implementationObject, ref );
+        final Object impl = ( m_tmpImplementationObject != null ) ? m_tmpImplementationObject : m_implementationObject;
+        dependencyManager.update( impl, ref );
     }
 
     void invokeBindMethod( DependencyManager dependencyManager, ServiceReference reference )
     {
-        dependencyManager.invokeBindMethod( m_implementationObject, reference);
+        final Object impl = ( m_tmpImplementationObject != null ) ? m_tmpImplementationObject : m_implementationObject;
+        dependencyManager.invokeBindMethod( impl, reference);
     }
 
     void invokeUnbindMethod( DependencyManager dependencyManager, ServiceReference oldRef )
     {
-        dependencyManager.invokeUnbindMethod( m_implementationObject, oldRef);
+        final Object impl = ( m_tmpImplementationObject != null ) ? m_tmpImplementationObject : m_implementationObject;
+        dependencyManager.invokeUnbindMethod( impl, oldRef);
     }
 
     protected void setFactoryProperties( Dictionary dictionary )
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 d6a5826..9e9f494 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
@@ -49,6 +49,11 @@
     // service instances
     private IdentityHashMap serviceContexts = new IdentityHashMap();
 
+    // pseudo map of implementation objects to be used for service
+    // binding while calling the activate method. The map's keys and values
+    // are just the implementation objects. The objects will only be
+    // contained while the activate method is being called.
+    private IdentityHashMap tmpImplementationObjects = new IdentityHashMap();
 
     /**
      * @param activator BundleComponentActivator for this DS implementation
@@ -140,11 +145,18 @@
         final BundleComponentContext serviceContext = new BundleComponentContext( this, bundle );
         Object service = createImplementationObject( serviceContext, new SetImplementationObject()
         {
-            public void setImplementationObject( Object implementationObject )
+            public void presetImplementationObject( Object implementationObject )
             {
                 serviceContext.setImplementationObject( implementationObject );
+                tmpImplementationObjects.put( implementationObject, serviceContext );
 
+            }
+
+
+            public void setImplementationObject( Object implementationObject )
+            {
                 serviceContexts.put( implementationObject, serviceContext );
+                tmpImplementationObjects.remove( implementationObject );
 
                 // if this is the first use of this component, switch to ACTIVE state
                 if ( getState() == STATE_REGISTERED )
@@ -154,11 +166,12 @@
             }
 
 
-            public void unsetImplementationObject( Object implementationObject )
+            public void resetImplementationObject( Object implementationObject )
             {
-                serviceContexts.remove( implementationObject );
+                tmpImplementationObjects.remove( implementationObject );
                 serviceContext.setImplementationObject( null );
             }
+
         } );
 
         // register the components component context if successfull
@@ -211,6 +224,11 @@
             Object implementationObject = it.next();
             dependencyManager.invokeBindMethod( implementationObject, reference);
         }
+        for ( Iterator it = tmpImplementationObjects.keySet().iterator(); it.hasNext(); )
+        {
+            Object implementationObject = it.next();
+            dependencyManager.invokeBindMethod( implementationObject, reference);
+        }
     }
 
     void invokeUnbindMethod( DependencyManager dependencyManager, ServiceReference oldRef )
@@ -220,6 +238,11 @@
             Object implementationObject = it.next();
             dependencyManager.invokeUnbindMethod( implementationObject, oldRef);
         }
+        for ( Iterator it = tmpImplementationObjects.keySet().iterator(); it.hasNext(); )
+        {
+            Object implementationObject = it.next();
+            dependencyManager.invokeUnbindMethod( implementationObject, oldRef);
+        }
     }
 
     protected MethodResult invokeModifiedMethod()
@@ -234,6 +257,14 @@
                     new ActivateMethod.ActivatorParameter( componentContext, -1 ), MethodResult.VOID );
 
         }
+        for (Iterator i = tmpImplementationObjects.values().iterator(); i.hasNext(); )
+        {
+            BundleComponentContext componentContext = ( BundleComponentContext ) i.next();
+            Object instance = componentContext.getInstance();
+            result = modifiedMethod.invoke( instance,
+                new ActivateMethod.ActivatorParameter( componentContext, -1 ), MethodResult.VOID );
+
+        }
         return result;
     }