Eliminate infinite busy wait in KarafIniWebEnvironment 25/68225/3
authorTom Pantelis <tompantelis@gmail.com>
Wed, 14 Feb 2018 01:01:13 +0000 (20:01 -0500)
committerTom Pantelis <tompantelis@gmail.com>
Wed, 14 Feb 2018 17:39:47 +0000 (12:39 -0500)
We've seen SFT's hang on shut down due to the busy wait if the
blueprint container for aaa-shiro fails or is destroyed while
being created. The busy wait blocks startup of the web app and also
blocks on shut down causing the hang. To alleviate this, use a
Future to obtain the AAAShiroProvider. Also added a BundleActivator
to register a blueprint event listener that fails the Future
exceptionally when the blueprint container is destroyed.

Change-Id: I9198579aa026ab685af73ee624c353d7097ba86e
Signed-off-by: Tom Pantelis <tompantelis@gmail.com>
aaa-shiro/impl/pom.xml
aaa-shiro/impl/src/main/java/org/opendaylight/aaa/AAAShiroActivator.java [new file with mode: 0644]
aaa-shiro/impl/src/main/java/org/opendaylight/aaa/AAAShiroProvider.java
aaa-shiro/impl/src/main/java/org/opendaylight/aaa/shiro/web/env/KarafIniWebEnvironment.java

index b259aba1b6fd679fa7f85796481f64f2c5c22c15..eaaf345dc9fb56ae0c4a6efba5775768ae6be44b 100644 (file)
@@ -262,6 +262,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
                             org.slf4j.*,
                             net.sf.ehcache.*,
                             org.jolokia.osgi.security,
+                            org.osgi.service.blueprint.container,
                             !javax.naming,
                             !javax.naming.spi,
                             !javax.servlet,
@@ -284,6 +285,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
                         </Import-Package>
                         <Embed-Dependency>h2;scope=compile|runtime;inline=true</Embed-Dependency>
                         <Web-ContextPath>/auth</Web-ContextPath>
+                        <Bundle-Activator>org.opendaylight.aaa.AAAShiroActivator</Bundle-Activator>
                     </instructions>
                 </configuration>
             </plugin>
diff --git a/aaa-shiro/impl/src/main/java/org/opendaylight/aaa/AAAShiroActivator.java b/aaa-shiro/impl/src/main/java/org/opendaylight/aaa/AAAShiroActivator.java
new file mode 100644 (file)
index 0000000..b1eb7e6
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2018 Inocybe Technologies and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.aaa;
+
+import java.util.Hashtable;
+import java.util.concurrent.CompletableFuture;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.blueprint.container.BlueprintEvent;
+import org.osgi.service.blueprint.container.BlueprintListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Bundle Activator.
+ *
+ * @author Thomas Pantelis
+ */
+public class AAAShiroActivator implements BundleActivator, BlueprintListener {
+    private static final Logger LOG = LoggerFactory.getLogger(AAAShiroActivator.class);
+
+    private ServiceRegistration<?> eventHandlerReg;
+    private Bundle thisBundle;
+
+    @Override
+    public void start(BundleContext context) {
+        thisBundle = context.getBundle();
+        eventHandlerReg = context.registerService(BlueprintListener.class.getName(), this, new Hashtable<>());
+    }
+
+    @Override
+    public void stop(BundleContext context) {
+        possiblyFailAAAShiroProviderFuture(String.format("%s bundle stopped", thisBundle));
+
+        if (eventHandlerReg != null) {
+            try {
+                eventHandlerReg.unregister();
+            } catch (IllegalStateException e) {
+                // This can be safely ignored
+            }
+        }
+    }
+
+    @Override
+    public void blueprintEvent(BlueprintEvent event) {
+        if (!thisBundle.equals(event.getBundle())) {
+            return;
+        }
+
+        LOG.debug("Blueprint container event for bundle {}: {}", thisBundle, event.getType());
+
+        if (event.getType() == BlueprintEvent.DESTROYING) {
+            possiblyFailAAAShiroProviderFuture(String.format("Blueprint container for bundle %s is being destroyed",
+                    thisBundle));
+        }
+    }
+
+    private static void possiblyFailAAAShiroProviderFuture(String message) {
+        CompletableFuture<AAAShiroProvider> instanceFuture = AAAShiroProvider.getInstanceFuture();
+        if (!instanceFuture.isDone()) {
+            instanceFuture.completeExceptionally(new RuntimeException(message));
+        }
+    }
+}
index 7118ab0db059455919d36876f61a88510985148d..709262dd9d8b247d7e0803ee8fc1fe0c34a0c9f5 100644 (file)
@@ -10,6 +10,7 @@ package org.opendaylight.aaa;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.Lists;
 import java.util.List;
+import java.util.concurrent.CompletableFuture;
 import javax.servlet.ServletException;
 import org.opendaylight.aaa.api.AuthenticationService;
 import org.opendaylight.aaa.api.CredentialAuth;
@@ -46,6 +47,7 @@ public class AAAShiroProvider {
 
     private static final Logger LOG = LoggerFactory.getLogger(AAAShiroProvider.class);
 
+    private static final CompletableFuture<AAAShiroProvider> INSTANCE_FUTURE = new CompletableFuture();
     private static volatile AAAShiroProvider INSTANCE;
     private static IIDMStore iidmStore;
 
@@ -165,6 +167,7 @@ public class AAAShiroProvider {
                                                final String dbPassword) {
         INSTANCE = new AAAShiroProvider(dataBroker, certificateManager, credentialAuth, shiroConfiguration,
                 httpService, moonEndpointPath, oauth2EndpointPath, datastoreConfig, dbUsername, dbPassword);
+        INSTANCE_FUTURE.complete(INSTANCE);
         return INSTANCE;
     }
 
@@ -177,6 +180,10 @@ public class AAAShiroProvider {
         return INSTANCE;
     }
 
+    public static CompletableFuture<AAAShiroProvider> getInstanceFuture() {
+        return INSTANCE_FUTURE;
+    }
+
     /**
      * Get IDM data store.
      *
index be7b34d275f6917192af55488366c980ed5627e5..dc33717fe206b0ea7eb85687549e74c03d6e6f0c 100644 (file)
@@ -9,6 +9,7 @@
 package org.opendaylight.aaa.shiro.web.env;
 
 import java.util.List;
+import java.util.concurrent.ExecutionException;
 import org.apache.shiro.SecurityUtils;
 import org.apache.shiro.config.Ini;
 import org.apache.shiro.config.IniSecurityManagerFactory;
@@ -64,17 +65,15 @@ public class KarafIniWebEnvironment extends IniWebEnvironment {
 
     @Override
     public void init() {
-        while (AAAShiroProvider.getInstance() == null) {
-            try {
-                Thread.sleep(50);
-            } catch (InterruptedException e) {
-                throw new RuntimeException("Interrupted waiting for b", e);
-            }
-        }
+        try {
+            AAAShiroProvider provider = AAAShiroProvider.getInstanceFuture().get();
 
-        // Initialize the Shiro environment from clustered-app-config
-        final Ini ini = createIniFromClusteredAppConfig(AAAShiroProvider.getInstance().getShiroConfiguration());
-        setIni(ini);
-        super.init();
+            // Initialize the Shiro environment from clustered-app-config
+            final Ini ini = createIniFromClusteredAppConfig(provider.getShiroConfiguration());
+            setIni(ini);
+            super.init();
+        } catch (InterruptedException | ExecutionException e) {
+            throw new RuntimeException("Error obtaining AAAShiroProvider", e);
+        }
     }
 }