Improve sync handling for service objects. Update to spec

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1691622 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/helper/ComponentServiceObjectsHelper.java b/scr/src/main/java/org/apache/felix/scr/impl/helper/ComponentServiceObjectsHelper.java
index 31ff915..634d192 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/helper/ComponentServiceObjectsHelper.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/helper/ComponentServiceObjectsHelper.java
@@ -18,11 +18,9 @@
  */
 package org.apache.felix.scr.impl.helper;
 
-
 import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.Collection;
 import java.util.List;
-import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
@@ -31,7 +29,6 @@
 import org.osgi.framework.ServiceReference;
 import org.osgi.service.component.ComponentServiceObjects;
 
-
 /**
  * Utility class for handling references using a ComponentServiceObjects
  * to get services.
@@ -40,7 +37,9 @@
 {
     private final BundleContext bundleContext;
 
-    private final Map<ServiceReference<?>, ComponentServiceObjectsImpl> services = new HashMap<ServiceReference<?>, ComponentServiceObjectsImpl>();
+    private final ConcurrentMap<ServiceReference<?>, ComponentServiceObjectsImpl> services = new ConcurrentHashMap<ServiceReference<?>, ComponentServiceObjectsImpl>();
+
+    private final List<ComponentServiceObjectsImpl> closedServices = new ArrayList<ComponentServiceObjectsImpl>();
 
     private final ConcurrentMap<ServiceReference, Object> prototypeInstances = new ConcurrentHashMap<ServiceReference, Object>();
 
@@ -51,43 +50,51 @@
 
     public void cleanup()
     {
-        synchronized ( this )
+    	Collection<ComponentServiceObjectsImpl> csos = services.values();
+        services.clear();
+        for(final ComponentServiceObjectsImpl cso : csos)
         {
-            for(final Map.Entry<ServiceReference<?>, ComponentServiceObjectsImpl> entry : services.entrySet())
-            {
-                entry.getValue().close();
-            }
-            services.clear();
+        	cso.deactivate();
+        }
+        synchronized ( this.closedServices )
+        {
+        	csos = new ArrayList<ComponentServiceObjectsImpl>(this.closedServices);
+        	this.closedServices.clear();
+        }
+        for(final ComponentServiceObjectsImpl cso : csos)
+        {
+        	cso.deactivate();
         }
         prototypeInstances.clear();
     }
 
     public ComponentServiceObjects getServiceObjects(final ServiceReference<?> ref)
     {
-        synchronized ( this )
+        ComponentServiceObjectsImpl cso = this.services.get(ref);
+        if ( cso == null )
         {
-            ComponentServiceObjectsImpl cso = this.services.get(ref);
-            if ( cso == null )
+            final ServiceObjects serviceObjects = this.bundleContext.getServiceObjects(ref);
+            if ( serviceObjects != null )
             {
-                final ServiceObjects serviceObjects = this.bundleContext.getServiceObjects(ref);
-                if ( serviceObjects != null )
+                cso = new ComponentServiceObjectsImpl(serviceObjects);
+                final ComponentServiceObjectsImpl oldCSO = this.services.putIfAbsent(ref, cso);
+                if ( oldCSO != null )
                 {
-                    cso = new ComponentServiceObjectsImpl(serviceObjects);
-                    this.services.put(ref, cso);
+                	cso = oldCSO;
                 }
             }
-            return cso;
         }
+        return cso;
     }
 
     public void closeServiceObjects(final ServiceReference<?> ref) {
-        ComponentServiceObjectsImpl cso;
-        synchronized ( this )
-        {
-            cso = this.services.remove(ref);
-        }
+        ComponentServiceObjectsImpl cso = this.services.remove(ref);
         if ( cso != null )
         {
+        	synchronized ( closedServices )
+        	{
+        		closedServices.add(cso);
+        	}
             cso.close();
         }
     }
@@ -115,11 +122,19 @@
 
         private volatile ServiceObjects serviceObjects;
 
+        private volatile boolean deactivated = false;
+
         public ComponentServiceObjectsImpl(final ServiceObjects so)
         {
             this.serviceObjects = so;
         }
 
+        public void deactivate()
+        {
+        	this.deactivated = true;
+        	close();
+        }
+
         /**
          * Close this instance and unget all services.
          */
@@ -129,30 +144,36 @@
             this.serviceObjects = null;
             if ( so != null )
             {
+            	final List<Object> localInstances = new ArrayList<Object>();
                 synchronized ( this.instances )
                 {
-                    for(final Object obj : instances)
+                	localInstances.addAll(this.instances);
+                	this.instances.clear();
+                }
+                for(final Object obj : localInstances)
+                {
+                    try
                     {
-                        try
-                        {
-                            so.ungetService(obj);
-                        }
-                        catch ( final IllegalStateException ise )
-                        {
-                            // ignore (this happens if the bundle is not valid anymore)
-                        }
-                        catch ( final IllegalArgumentException iae )
-                        {
-                            // ignore (this happens if the service has not been returned by the service objects)
-                        }
+                        so.ungetService(obj);
                     }
-                    this.instances.clear();
+                    catch ( final IllegalStateException ise )
+                    {
+                        // ignore (this happens if the bundle is not valid anymore)
+                    }
+                    catch ( final IllegalArgumentException iae )
+                    {
+                        // ignore (this happens if the service has not been returned by the service objects)
+                    }
                 }
             }
         }
 
         public Object getService()
         {
+        	if ( this.deactivated )
+        	{
+        		throw new IllegalStateException();
+        	}
             final ServiceObjects so = this.serviceObjects;
             Object service = null;
             if ( so != null )
@@ -171,6 +192,10 @@
 
         public void ungetService(final Object service)
         {
+        	if ( this.deactivated )
+        	{
+        		throw new IllegalStateException();
+        	}
             final ServiceObjects so = this.serviceObjects;
             if ( so != null )
             {
@@ -194,5 +219,12 @@
             }
             return null;
         }
+
+		@Override
+		public String toString() {
+			return "ComponentServiceObjectsImpl [instances=" + instances + ", serviceObjects=" + serviceObjects
+					+ ", deactivated=" + deactivated + ", hashCode=" + this.hashCode() + "]";
+		}
+
     }
  }