Bug 7326: Fix ConcurrentModificationException in Blueprint 54/49154/4
authorTom Pantelis <tpanteli@brocade.com>
Fri, 9 Dec 2016 15:35:51 +0000 (10:35 -0500)
committerTom Pantelis <tpanteli@brocade.com>
Fri, 9 Dec 2016 15:35:51 +0000 (10:35 -0500)
in AbstractDependentComponentFactoryMetadata.stopServiceRecipes()

tpantelis: "This is an edge case where the container is destroyed
immediately after and while it's starting up. This isn't likely to
happen in production but can happen during feature tests. I had assumed
the container would provide the protection but apparently it doesn't."

Change-Id: Id7532d30cb0a5f67fd907cb15372069d8769b247
Signed-off-by: Michael Vorburger <vorburger@redhat.com>
Signed-off-by: Tom Pantelis <tpanteli@brocade.com>
opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/AbstractDependentComponentFactoryMetadata.java

index c55fa0ce913147b8d554a40693ededf97359aa10..c15a7037e7c199b855a1e374867c9d21d3358f5f 100644 (file)
@@ -14,6 +14,7 @@ import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Consumer;
 import javax.annotation.Nullable;
+import javax.annotation.concurrent.GuardedBy;
 import org.apache.aries.blueprint.di.AbstractRecipe;
 import org.apache.aries.blueprint.di.ExecutionContext;
 import org.apache.aries.blueprint.di.Recipe;
@@ -36,12 +37,15 @@ abstract class AbstractDependentComponentFactoryMetadata implements DependentCom
     private final AtomicBoolean started = new AtomicBoolean();
     private final AtomicBoolean satisfied = new AtomicBoolean();
     private final AtomicBoolean restarting = new AtomicBoolean();
+    @GuardedBy("serviceRecipes")
     private final List<StaticServiceReferenceRecipe> serviceRecipes = new ArrayList<>();
     private volatile ExtendedBlueprintContainer container;
     private volatile SatisfactionCallback satisfactionCallback;
     private volatile String failureMessage;
     private volatile Throwable failureCause;
     private volatile String dependendencyDesc;
+    @GuardedBy("serviceRecipes")
+    private boolean stoppedServiceRecipes;
 
     protected AbstractDependentComponentFactoryMetadata(String id) {
         this.id = Preconditions.checkNotNull(id);
@@ -100,12 +104,18 @@ abstract class AbstractDependentComponentFactoryMetadata implements DependentCom
     }
 
     protected void retrieveService(String name, String interfaceName, Consumer<Object> onServiceRetrieved) {
-        StaticServiceReferenceRecipe recipe = new StaticServiceReferenceRecipe(getId() + "-" + name,
-                container, interfaceName);
-        setDependendencyDesc(recipe.getOsgiFilter());
-        serviceRecipes.add(recipe);
+        synchronized (serviceRecipes) {
+            if (stoppedServiceRecipes) {
+                return;
+            }
+
+            StaticServiceReferenceRecipe recipe = new StaticServiceReferenceRecipe(getId() + "-" + name,
+                    container, interfaceName);
+            setDependendencyDesc(recipe.getOsgiFilter());
+            serviceRecipes.add(recipe);
 
-        recipe.startTracking(onServiceRetrieved);
+            recipe.startTracking(onServiceRetrieved);
+        }
     }
 
     protected final String logName() {
@@ -189,11 +199,14 @@ abstract class AbstractDependentComponentFactoryMetadata implements DependentCom
     }
 
     private void stopServiceRecipes() {
-        for (StaticServiceReferenceRecipe recipe: serviceRecipes) {
-            recipe.stop();
-        }
+        synchronized (serviceRecipes) {
+            stoppedServiceRecipes = true;
+            for (StaticServiceReferenceRecipe recipe: serviceRecipes) {
+                recipe.stop();
+            }
 
-        serviceRecipes.clear();
+            serviceRecipes.clear();
+        }
     }
 
     protected void restartContainer() {