FELIX-3729 fix various bugs

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1424302 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/helper/BindMethod.java b/scr/src/main/java/org/apache/felix/scr/impl/helper/BindMethod.java
index 86a8a1f..2c0f91b 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/helper/BindMethod.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/helper/BindMethod.java
@@ -563,6 +563,7 @@
                 Object service = context.getService( refPair.getRef() );
                 if ( service == null )
                 {
+                    refPair.setFailed();
                     getLogger().log(
                          LogService.LOG_WARNING,
                          "Could not get service from ref " + refPair.getRef(), null );
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 3d5c070..bf323d6 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
@@ -26,6 +26,7 @@
 import java.util.Dictionary;
 import java.util.List;
 import java.util.SortedMap;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 
 import org.apache.felix.scr.Component;
@@ -185,6 +186,10 @@
         {
         }
 
+        public void removingService( ServiceReference<T> item, RefPair<T> object, int size )
+        {
+        }
+
         public void removedService( ServiceReference<T> serviceReference, RefPair<T> refPair )
         {
             if ( !isOptional() )
@@ -221,7 +226,10 @@
             RefPair<T> refPair = new RefPair<T>( serviceReference  );
             if (isActive())
             {
-                 m_bindMethods.getBind().getServiceObject( refPair, m_componentManager.getActivator().getBundleContext());
+                 if (!m_bindMethods.getBind().getServiceObject( refPair, m_componentManager.getActivator().getBundleContext()))
+                 {
+                      m_componentManager.getActivator().registerMissingDependency( DependencyManager.this, serviceReference );
+                 }
             }
             return refPair;
         }
@@ -230,7 +238,10 @@
         {
             if (isActive())
             {
-                m_componentManager.invokeBindMethod( DependencyManager.this, refPair );
+                if ( !refPair.isFailed() )
+                {
+                    m_componentManager.invokeBindMethod( DependencyManager.this, refPair );
+                }
             }
             else if ( isTrackerOpened() && !isOptional() )
             {
@@ -246,18 +257,11 @@
             }
         }
 
-        public void removedService( ServiceReference<T> serviceReference, RefPair<T> refPair )
+        public void removingService( ServiceReference<T> item, RefPair<T> refPair, int size )
         {
             if ( isActive() )
             {
-                boolean unbind = true;
-                if ( !isOptional() )
-                {
-                    if (getTracker().isEmpty())
-                    {
-                        unbind = false;
-                    }
-                }
+                boolean unbind = isOptional() || size > 0;
                 if ( unbind )
                 {
                     m_componentManager.invokeUnbindMethod( DependencyManager.this, refPair );
@@ -267,6 +271,10 @@
                     m_componentManager.deactivateInternal( ComponentConstants.DEACTIVATION_REASON_REFERENCE, false );
                 }
             }
+        }
+
+        public void removedService( ServiceReference<T> serviceReference, RefPair<T> refPair )
+        {
             if (refPair.getServiceObject() != null)
             {
                 m_componentManager.getActivator().getBundleContext().ungetService( serviceReference );
@@ -281,7 +289,14 @@
             {
                 synchronized (refPair)
                 {
-                    success |= m_bindMethods.getBind().getServiceObject( refPair, m_componentManager.getActivator().getBundleContext());
+                    if (m_bindMethods.getBind().getServiceObject( refPair, m_componentManager.getActivator().getBundleContext()))
+                    {
+                         success = true;
+                    }
+                    else
+                    {
+                         m_componentManager.getActivator().registerMissingDependency( DependencyManager.this, refPair.getRef() );
+                    }
                 }
             }
             return success;
@@ -309,6 +324,9 @@
     private class MultipleStaticGreedyCustomizer extends AbstractCustomizer {
 
 
+        private final AtomicInteger reactivateCount = new AtomicInteger(  );
+
+
         public RefPair<T> addingService( ServiceReference<T> serviceReference )
         {
             RefPair<T> refPair = new RefPair<T>( serviceReference  );
@@ -344,16 +362,31 @@
             }
         }
 
-        public void removedService( ServiceReference<T> serviceReference, RefPair<T> refPair )
+        public void removingService( ServiceReference<T> item, RefPair<T> object, int size )
         {
             if ( isActive() )
             {
+                //deactivate while ref is still tracked
                     m_componentManager.log( LogService.LOG_DEBUG,
-                        "Dependency Manager: Static dependency on {0}/{1} is broken", new Object[]
-                            { m_dependencyMetadata.getName(), m_dependencyMetadata.getInterface() }, null );
+                            "Dependency Manager: Static dependency on {0}/{1} is broken", new Object[]
+                            {m_dependencyMetadata.getName(), m_dependencyMetadata.getInterface()}, null );
                     m_componentManager.deactivateInternal( ComponentConstants.DEACTIVATION_REASON_REFERENCE, false );
-                    m_componentManager.activateInternal();
+                    reactivateCount.incrementAndGet();
+            }
+        }
 
+        public void removedService( ServiceReference<T> serviceReference, RefPair<T> refPair )
+        {
+            if ( reactivateCount.getAndDecrement() > 0 )
+            {
+                //try to reactivate after ref is no longer tracked.
+                m_componentManager.activateInternal();
+
+            }
+            //This is unlikely
+            if (refPair.getServiceObject() != null)
+            {
+                m_componentManager.getActivator().getBundleContext().ungetService( serviceReference );
             }
         }
 
@@ -416,12 +449,17 @@
             }
         }
 
+        public void removingService( ServiceReference<T> item, RefPair<T> object, int size )
+        {
+        }
+
         public void removedService( ServiceReference<T> serviceReference, RefPair<T> refPair )
         {
             if ( isActive() )
             {
                 if (refs.contains( refPair ))
                 {
+                    //we are tracking the used refs, so we can deactivate here.
                     m_componentManager.log( LogService.LOG_DEBUG,
                         "Dependency Manager: Static dependency on {0}/{1} is broken", new Object[]
                             { m_dependencyMetadata.getName(), m_dependencyMetadata.getInterface() }, null );
@@ -491,16 +529,20 @@
                 {
                     synchronized ( refPair )
                     {
-                        if (!m_bindMethods.getBind().getServiceObject( refPair, m_componentManager.getActivator().getBundleContext() ))
+                        m_bindMethods.getBind().getServiceObject( refPair, m_componentManager.getActivator().getBundleContext() );
+                    }
+                    if ( !refPair.isFailed() )
+                    {
+                        m_componentManager.invokeBindMethod( DependencyManager.this, refPair );
+                        if ( this.refPair != null )
                         {
-                            //TODO error???
+                            m_componentManager.invokeUnbindMethod( DependencyManager.this, this.refPair );
+                            closeRefPair();
                         }
                     }
-                    m_componentManager.invokeBindMethod( DependencyManager.this, refPair );
-                    if ( this.refPair != null )
+                    else
                     {
-                        m_componentManager.invokeUnbindMethod( DependencyManager.this, this.refPair );
-                        closeRefPair();
+                        m_componentManager.getActivator().registerMissingDependency( DependencyManager.this, serviceReference );
                     }
                     this.refPair = refPair;
                 }
@@ -519,6 +561,10 @@
             }
         }
 
+        public void removingService( ServiceReference<T> item, RefPair<T> object, int size )
+        {
+        }
+
         public void removedService( ServiceReference<T> serviceReference, RefPair<T> refPair )
         {
             if (refPair == this.refPair)
@@ -537,7 +583,10 @@
                                 //TODO error???
                             }
                         }
-                        m_componentManager.invokeBindMethod( DependencyManager.this, nextRefPair );
+                        if ( !refPair.isFailed() )
+                        {
+                            m_componentManager.invokeBindMethod( DependencyManager.this, nextRefPair );
+                        }
                     }
 
                     if ( isOptional() || nextRefPair != null)
@@ -567,6 +616,10 @@
                     {
                         success |= m_bindMethods.getBind().getServiceObject( refPair, m_componentManager.getActivator().getBundleContext() );
                     }
+                    if (refPair.isFailed())
+                    {
+                        m_componentManager.getActivator().registerMissingDependency( DependencyManager.this, refPair.getRef() );
+                    }
                     this.refPair = refPair;
                 }
             }
@@ -630,6 +683,10 @@
             }
         }
 
+        public void removingService( ServiceReference<T> item, RefPair<T> object, int size )
+        {
+        }
+
         public void removedService( ServiceReference<T> serviceReference, RefPair<T> refPair )
         {
             if ( isActive() && refPair == this.refPair )
@@ -1291,12 +1348,6 @@
         return null;
     }
 
-    private int getServiceReferenceCount()
-    {
-        ServiceReference<T>[] refs = getFrameworkServiceReferences();
-        return refs == null? 0: refs.length;
-    }
-
 
     /**
      * Returns a <code>ServiceReference</code> instances for a service
@@ -1695,14 +1746,17 @@
             new Object[]{ m_dependencyMetadata.getName(), success, refs }, null );
         for ( RefPair<T> refPair : refs )
         {
-            if ( !invokeBindMethod( componentInstance, refPair ) )
+            if ( !refPair.isFailed() )
             {
-                m_componentManager.log( LogService.LOG_DEBUG,
-                        "For dependency {0}, failed to invoke bind method on object {1}",
-                        new Object[] {m_dependencyMetadata.getName(), refPair}, null );
+                if ( !invokeBindMethod( componentInstance, refPair ) )
+                {
+                    m_componentManager.log( LogService.LOG_DEBUG,
+                            "For dependency {0}, failed to invoke bind method on object {1}",
+                            new Object[] {m_dependencyMetadata.getName(), refPair}, null );
 
+                }
+                success = true;
             }
-            success = true;
         }
         return success;
     }
@@ -1790,6 +1844,7 @@
         }
         if ( !isMultiple() )
         {
+            //TODO fixme to use tracker
             ServiceReference<T>[] refs = getFrameworkServiceReferences();
             if ( refs == null )
             {
@@ -1807,6 +1862,7 @@
         }
         //TODO static and dynamic reluctant
         RefPair<T> refPair = trackerRef.get().getService( ref );
+        m_bindMethods.getBind().getServiceObject( refPair, m_componentManager.getActivator().getBundleContext() );
         m_componentManager.invokeBindMethod( this, refPair );
     }
 
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/manager/RefPair.java b/scr/src/main/java/org/apache/felix/scr/impl/manager/RefPair.java
index 1b2e1e3..a767d1c 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/manager/RefPair.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/manager/RefPair.java
@@ -30,6 +30,8 @@
     private final ServiceReference<T> ref;
     private T serviceObject;
 
+    private boolean failed;
+
     public RefPair( ServiceReference<T> ref )
     {
         this.ref = ref;
@@ -48,8 +50,23 @@
     public void setServiceObject( T serviceObject )
     {
         this.serviceObject = serviceObject;
+        if ( serviceObject != null)
+        {
+            failed = false;
+        }
     }
 
+    public void setFailed( )
+    {
+        this.failed = true;
+    }
+
+    public boolean isFailed()
+    {
+        return failed;
+    }
+
+
     @Override
     public String toString()
     {
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/manager/ServiceTracker.java b/scr/src/main/java/org/apache/felix/scr/impl/manager/ServiceTracker.java
index 178f2c5..a71066f 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/manager/ServiceTracker.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/manager/ServiceTracker.java
@@ -981,6 +981,11 @@
         private final LinkedList<S> initial;
 
         /**
+         * counter to show size after removed during remmoving event.
+         */
+        private int pendingRemovals;
+
+        /**
          * AbstractTracked constructor.
          */
         AbstractTracked() {
@@ -1182,6 +1187,7 @@
          */
         void untrack(final S item, final R related) {
             final T object;
+            final int size;
             synchronized (this) {
                 if (initial.remove(item)) { /*
                                              * if this item is already in the list
@@ -1208,7 +1214,16 @@
                              * adding
                              */
                 }
-                object = tracked.remove(item); /*
+                object = tracked.get( item );
+                pendingRemovals++;
+                size = tracked.size() - pendingRemovals;
+            }
+            /* Call customizer outside of synchronized region */
+            customizerRemoving(item, related, object, size );
+            synchronized (this) {
+                pendingRemovals--;
+
+                tracked.remove(item); /*
                                                  * must remove from tracker before
                                                  * calling customizer callback
                                                  */
@@ -1336,6 +1351,8 @@
          */
         abstract void customizerModified(final S item, final R related, final T object);
 
+        abstract void customizerRemoving( final S item, final R related, final T object, int size );
+
         /**
          * Call the specific customizer removed method. This method must not be
          * called while synchronized on this object.
@@ -1441,7 +1458,13 @@
 			customizer.modifiedService(item, object);
 		}
 
-		/**
+        @Override
+        void customizerRemoving( ServiceReference<S> item, ServiceEvent related, T object, int size )
+        {
+            customizer.removingService( item, object, size );
+        }
+
+        /**
 		 * Call the specific customizer removed method. This method must not be
 		 * called while synchronized on this object.
 		 * 
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/manager/ServiceTrackerCustomizer.java b/scr/src/main/java/org/apache/felix/scr/impl/manager/ServiceTrackerCustomizer.java
index ac35526..8809db6 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/manager/ServiceTrackerCustomizer.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/manager/ServiceTrackerCustomizer.java
@@ -81,6 +81,8 @@
 	 */
 	public void modifiedService(ServiceReference<S> reference, T service);
 
+    void removingService( ServiceReference<S> item, T object, int size );
+
 	/**
 	 * A service tracked by the {@code ServiceTracker} has been removed.
 	 * 
@@ -92,4 +94,5 @@
 	 * @param service The service object for the specified referenced service.
 	 */
 	public void removedService(ServiceReference<S> reference, T service);
+
 }