summaryrefslogtreecommitdiffstatsabout
diff options
context:
space:
mode:
authorOlaf Otto2013-03-19 11:19:07 (EDT)
committer Glyn Normington2013-03-19 11:19:07 (EDT)
commite552de0a5cf17cabdc2db9c8f9470d27dafa96c4 (patch)
tree2e57dbd4d50b1d552ca824e8b94e627fc9fee417
parent47e5abfc67ccc093219eb25e9543c75f239188af (diff)
downloadorg.eclipse.gemini.blueprint-e552de0a5cf17cabdc2db9c8f9470d27dafa96c4.zip
org.eclipse.gemini.blueprint-e552de0a5cf17cabdc2db9c8f9470d27dafa96c4.tar.gz
org.eclipse.gemini.blueprint-e552de0a5cf17cabdc2db9c8f9470d27dafa96c4.tar.bz2
403628: support synchronous application context shutdown
-rw-r--r--docs/src/docbkx/reference/deployment.xml12
-rw-r--r--extender/src/main/java/org/eclipse/gemini/blueprint/extender/internal/activator/LifecycleManager.java40
-rw-r--r--extender/src/main/java/org/eclipse/gemini/blueprint/extender/internal/support/ExtenderConfiguration.java35
-rw-r--r--extender/src/test/java/org/eclipse/gemini/blueprint/extender/internal/activator/LifecycleManagerTest.java135
-rw-r--r--extender/src/test/java/org/eclipse/gemini/blueprint/extender/internal/support/ExtenderConfigurationCustomSettingsTest.java6
-rw-r--r--extender/src/test/java/org/eclipse/gemini/blueprint/extender/internal/support/ExtenderConfigurationDefaultSettingsTest.java6
-rw-r--r--extender/src/test/resources/org/eclipse/gemini/blueprint/extender/internal/support/extender-custom-config.xml3
7 files changed, 216 insertions, 21 deletions
diff --git a/docs/src/docbkx/reference/deployment.xml b/docs/src/docbkx/reference/deployment.xml
index 3177ef9..5b3263e 100644
--- a/docs/src/docbkx/reference/deployment.xml
+++ b/docs/src/docbkx/reference/deployment.xml
@@ -469,7 +469,17 @@ Blueprint-Bundle: config/account-data-context.xml, config/osgi-*.xml
<entry>The amount of time the extender will wait for each application context to shutdown gracefully. Expressed in milliseconds.</entry>
<entry>10000 ms (10 s)</entry>
</row>
-
+
+ <row>
+ <entry><literal>shutdown.asynchronously</literal></entry>
+ <entry><classname>boolean</classname></entry>
+ <entry>
+ Whether the extender shall shutdown each application context asynchronously. If <literal>false</literal>,
+ the <literal>shutdown.wait.time</literal> is ignored. Accordingly, application contexts blocking during close will block the bundle shutdown.
+ </entry>
+ <entry><literal>true</literal></entry>
+ </row>
+
<row>
<entry><literal>dependencies.wait.time</literal></entry>
<entry><classname>java.lang.Long</classname></entry>
diff --git a/extender/src/main/java/org/eclipse/gemini/blueprint/extender/internal/activator/LifecycleManager.java b/extender/src/main/java/org/eclipse/gemini/blueprint/extender/internal/activator/LifecycleManager.java
index b2d5e43..2b13cce 100644
--- a/extender/src/main/java/org/eclipse/gemini/blueprint/extender/internal/activator/LifecycleManager.java
+++ b/extender/src/main/java/org/eclipse/gemini/blueprint/extender/internal/activator/LifecycleManager.java
@@ -271,28 +271,42 @@ class LifecycleManager implements DisposableBean {
* @param bundle
*/
protected void maybeCloseApplicationContextFor(Bundle bundle) {
- final ConfigurableOsgiBundleApplicationContext context =
- (ConfigurableOsgiBundleApplicationContext) managedContexts.remove(Long.valueOf(bundle.getBundleId()));
+ final ConfigurableOsgiBundleApplicationContext context = managedContexts.remove(bundle.getBundleId());
if (context == null) {
return;
}
- RunnableTimedExecution.execute(new Runnable() {
+ maybeClose(context);
+ }
- private final String toString = "Closing runnable for context " + context.getDisplayName();
+ protected void maybeClose(final ConfigurableOsgiBundleApplicationContext context) {
+ final String displayName = context.getDisplayName();
+ Runnable shutdownTask = new Runnable() {
- public void run() {
- closeApplicationContext(context);
- }
+ private final String toString = "Closing runnable for context " + displayName;
- public String toString() {
- return toString;
- }
+ public void run() {
+ closeApplicationContext(context);
+ }
- }, extenderConfiguration.getShutdownWaitTime(), shutdownTaskExecutor);
- }
+ public String toString() {
+ return toString;
+ }
- /**
+ };
+
+ if (extenderConfiguration.shouldShutdownAsynchronously()) {
+ RunnableTimedExecution.execute(shutdownTask, extenderConfiguration.getShutdownWaitTime(), shutdownTaskExecutor);
+ } else {
+ try {
+ shutdownTask.run();
+ } catch (Exception e) {
+ log.error(displayName + " context shutdown failed.", e);
+ }
+ }
+ }
+
+ /**
* Closes an application context. This is a convenience methods that invokes the event notification as well.
*
* @param ctx
diff --git a/extender/src/main/java/org/eclipse/gemini/blueprint/extender/internal/support/ExtenderConfiguration.java b/extender/src/main/java/org/eclipse/gemini/blueprint/extender/internal/support/ExtenderConfiguration.java
index 78560ce..765be66 100644
--- a/extender/src/main/java/org/eclipse/gemini/blueprint/extender/internal/support/ExtenderConfiguration.java
+++ b/extender/src/main/java/org/eclipse/gemini/blueprint/extender/internal/support/ExtenderConfiguration.java
@@ -42,7 +42,12 @@ import org.springframework.util.ObjectUtils;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Properties;
+import java.util.Timer;
/**
* Configuration class for the extender. Takes care of locating the extender specific configurations and merging the
@@ -67,6 +72,8 @@ public class ExtenderConfiguration implements BundleActivator {
private static final String PROPERTIES_NAME = "extenderProperties";
+ private static final String SHUTDOWN_ASYNCHRONOUS_KEY = "shutdown.asynchronously";
+
private static final String SHUTDOWN_WAIT_KEY = "shutdown.wait.time";
private static final String PROCESS_ANNOTATIONS_KEY = "process.annotations";
@@ -91,7 +98,9 @@ public class ExtenderConfiguration implements BundleActivator {
// default dependency wait time (in milliseconds)
private static final long DEFAULT_DEP_WAIT = ConfigUtils.DIRECTIVE_TIMEOUT_DEFAULT * 1000;
private static final boolean DEFAULT_NS_BUNDLE_STATE = true;
- private static final long DEFAULT_SHUTDOWN_WAIT = 10 * 1000;
+ private static final boolean DEFAULT_SHUTDOWN_ASYNCHRONOUS = true;
+ private static final long DEFAULT_SHUTDOWN_WAIT = 10 * 1000;
+
private static final boolean DEFAULT_PROCESS_ANNOTATION = false;
private ConfigurableOsgiBundleApplicationContext extenderConfiguration;
@@ -106,6 +115,8 @@ public class ExtenderConfiguration implements BundleActivator {
private long shutdownWaitTime, dependencyWaitTime;
+ private boolean shutdownAsynchronously;
+
private boolean processAnnotation, nsBundledResolved;
private OsgiBundleApplicationContextEventMulticaster eventMulticaster;
@@ -128,7 +139,7 @@ public class ExtenderConfiguration implements BundleActivator {
// fields reading/writing lock
private final Object lock = new Object();
- /**
+ /**
* Constructs a new <code>ExtenderConfiguration</code> instance. Locates the extender configuration, creates an
* application context which will returned the extender items.
*
@@ -209,7 +220,8 @@ public class ExtenderConfiguration implements BundleActivator {
synchronized (lock) {
shutdownWaitTime = getShutdownWaitTime(properties);
- dependencyWaitTime = getDependencyWaitTime(properties);
+ shutdownAsynchronously = getShutdownAsynchronously(properties);
+ dependencyWaitTime = getDependencyWaitTime(properties);
processAnnotation = getProcessAnnotations(properties);
}
@@ -302,6 +314,7 @@ public class ExtenderConfiguration implements BundleActivator {
private Properties createDefaultProperties() {
Properties properties = new Properties();
properties.setProperty(SHUTDOWN_WAIT_KEY, "" + DEFAULT_SHUTDOWN_WAIT);
+ properties.setProperty(SHUTDOWN_ASYNCHRONOUS_KEY, "" + DEFAULT_SHUTDOWN_ASYNCHRONOUS);
properties.setProperty(PROCESS_ANNOTATIONS_KEY, "" + DEFAULT_PROCESS_ANNOTATION);
properties.setProperty(WAIT_FOR_DEPS_TIMEOUT_KEY, "" + DEFAULT_DEP_WAIT);
@@ -388,6 +401,10 @@ public class ExtenderConfiguration implements BundleActivator {
return Long.parseLong(properties.getProperty(SHUTDOWN_WAIT_KEY));
}
+ private boolean getShutdownAsynchronously(Properties properties) {
+ return Boolean.valueOf(properties.getProperty(SHUTDOWN_ASYNCHRONOUS_KEY));
+ }
+
private long getDependencyWaitTime(Properties properties) {
return Long.parseLong(properties.getProperty(WAIT_FOR_DEPS_TIMEOUT_KEY));
}
@@ -452,6 +469,16 @@ public class ExtenderConfiguration implements BundleActivator {
}
}
+ /**
+ * @return whether the application context shutdown during the bundle stop phase shall be
+ * performed asynchronously.
+ */
+ public boolean shouldShutdownAsynchronously() {
+ synchronized (lock) {
+ return this.shutdownAsynchronously;
+ }
+ }
+
/**
* Returns the dependencyWaitTime.
*
diff --git a/extender/src/test/java/org/eclipse/gemini/blueprint/extender/internal/activator/LifecycleManagerTest.java b/extender/src/test/java/org/eclipse/gemini/blueprint/extender/internal/activator/LifecycleManagerTest.java
new file mode 100644
index 0000000..b7e2cc6
--- /dev/null
+++ b/extender/src/test/java/org/eclipse/gemini/blueprint/extender/internal/activator/LifecycleManagerTest.java
@@ -0,0 +1,135 @@
+/******************************************************************************
+ * Copyright (c) 2013 Olaf Otto
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html and the Apache License v2.0
+ * is available at http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * Olaf Otto
+ *****************************************************************************/
+package org.eclipse.gemini.blueprint.extender.internal.activator;
+
+import junit.framework.TestCase;
+import org.easymock.MockControl;
+import org.eclipse.gemini.blueprint.context.ConfigurableOsgiBundleApplicationContext;
+import org.eclipse.gemini.blueprint.extender.internal.support.ExtenderConfiguration;
+
+import static java.lang.Thread.yield;
+
+/**
+ * @author Olaf Otto
+ */
+public class LifecycleManagerTest extends TestCase {
+ private MockControl contextControl = MockControl.createControl(ConfigurableOsgiBundleApplicationContext.class);
+ private ConfigurableOsgiBundleApplicationContext context = (ConfigurableOsgiBundleApplicationContext) contextControl.getMock();
+
+ private MockControl osgiContextProcessorControl = MockControl.createControl(OsgiContextProcessor.class);
+ private OsgiContextProcessor osgiContextProcessor = (OsgiContextProcessor) osgiContextProcessorControl.getMock();
+
+ private boolean shouldShutdownAsynchronously;
+
+ // Cannot mock a class with easymock 1.2
+ ExtenderConfiguration configuration = new ExtenderConfiguration() {
+ @Override
+ public boolean shouldShutdownAsynchronously() {
+ return shouldShutdownAsynchronously;
+ }
+
+ @Override
+ public long getShutdownWaitTime() {
+ return 0L;
+ }
+ };
+
+ private LifecycleManager testee = new LifecycleManager(this.configuration, null, null, null, this.osgiContextProcessor, null, null);
+
+ public void testSuccessfulAsynchronousShutdown() throws Exception {
+ withAsynchronousShutdownDisabled();
+ withSuccessfulContextClose();
+ withPreAndPostProcessing();
+
+ shutdownContext();
+
+ verifyContextIsClosed();
+ verifyOsgiContextProcessorInteractions();
+ }
+
+ public void testSuccessfulSynchronousShutdown() throws Exception {
+ withAsynchronousShutdownEnabled();
+ withSuccessfulContextClose();
+ withPreAndPostProcessing();
+
+ shutdownContext();
+
+ verifyContextIsClosed();
+ verifyOsgiContextProcessorInteractions();
+ }
+
+ public void testFailingAsynchronousShutdown() throws Exception {
+ withAsynchronousShutdownEnabled();
+ withFailingApplicationContextClose();
+ withPreAndPostProcessing();
+
+ shutdownContext();
+ yield();
+
+ verifyContextIsClosed();
+ verifyOsgiContextProcessorInteractions();
+ }
+
+ public void testFailingSynchronousShutdown() {
+ withAsynchronousShutdownDisabled();
+ withFailingApplicationContextClose();
+ withPreAndPostProcessing();
+
+ shutdownContext();
+
+ verifyContextIsClosed();
+ verifyOsgiContextProcessorInteractions();
+ }
+
+ private void withFailingApplicationContextClose() {
+ this.context.getDisplayName();
+ this.contextControl.setReturnValue("Display name");
+ this.context.close();
+ this.contextControl.setThrowable(new RuntimeException("THIS IS AN EXPECTED TEST EXCEPTION"));
+ }
+
+ private void verifyContextIsClosed() {
+ this.contextControl.verify();
+ }
+
+ private void verifyOsgiContextProcessorInteractions() {
+ this.osgiContextProcessorControl.verify();
+ }
+
+ private void withPreAndPostProcessing() {
+ this.osgiContextProcessor.preProcessClose(this.context);
+ this.osgiContextProcessor.postProcessClose(this.context);
+ }
+
+ private void withSuccessfulContextClose() {
+ this.context.close();
+ this.context.getDisplayName();
+ this.contextControl.setDefaultReturnValue("Nothing");
+ }
+
+ private void shutdownContext() {
+ this.contextControl.replay();
+ this.osgiContextProcessorControl.replay();
+
+ this.testee.maybeClose(this.context);
+ }
+
+ private void withAsynchronousShutdownDisabled() {
+ this.shouldShutdownAsynchronously = false;
+ }
+
+ private void withAsynchronousShutdownEnabled() {
+ this.shouldShutdownAsynchronously = true;
+ }
+}
diff --git a/extender/src/test/java/org/eclipse/gemini/blueprint/extender/internal/support/ExtenderConfigurationCustomSettingsTest.java b/extender/src/test/java/org/eclipse/gemini/blueprint/extender/internal/support/ExtenderConfigurationCustomSettingsTest.java
index 8a58d2a..ece5843 100644
--- a/extender/src/test/java/org/eclipse/gemini/blueprint/extender/internal/support/ExtenderConfigurationCustomSettingsTest.java
+++ b/extender/src/test/java/org/eclipse/gemini/blueprint/extender/internal/support/ExtenderConfigurationCustomSettingsTest.java
@@ -80,7 +80,11 @@ public class ExtenderConfigurationCustomSettingsTest extends TestCase {
assertEquals(300, config.getShutdownWaitTime());
}
- public void testShouldProcessAnnotation() throws Exception {
+ public void testShutdownAsynchronously() throws Exception {
+ assertFalse(config.shouldShutdownAsynchronously());
+ }
+
+ public void testShouldProcessAnnotation() throws Exception {
assertTrue(config.shouldProcessAnnotation());
}
diff --git a/extender/src/test/java/org/eclipse/gemini/blueprint/extender/internal/support/ExtenderConfigurationDefaultSettingsTest.java b/extender/src/test/java/org/eclipse/gemini/blueprint/extender/internal/support/ExtenderConfigurationDefaultSettingsTest.java
index 7f02cb5..736b04d 100644
--- a/extender/src/test/java/org/eclipse/gemini/blueprint/extender/internal/support/ExtenderConfigurationDefaultSettingsTest.java
+++ b/extender/src/test/java/org/eclipse/gemini/blueprint/extender/internal/support/ExtenderConfigurationDefaultSettingsTest.java
@@ -66,7 +66,11 @@ public class ExtenderConfigurationDefaultSettingsTest extends TestCase {
assertEquals(10 * 1000, config.getShutdownWaitTime());
}
- public void testShouldProcessAnnotation() throws Exception {
+ public void testShutdownAsynchronously() throws Exception {
+ assertTrue(config.shouldShutdownAsynchronously());
+ }
+
+ public void testShouldProcessAnnotation() throws Exception {
assertFalse(config.shouldProcessAnnotation());
}
diff --git a/extender/src/test/resources/org/eclipse/gemini/blueprint/extender/internal/support/extender-custom-config.xml b/extender/src/test/resources/org/eclipse/gemini/blueprint/extender/internal/support/extender-custom-config.xml
index d6157b6..1d4ad3c 100644
--- a/extender/src/test/resources/org/eclipse/gemini/blueprint/extender/internal/support/extender-custom-config.xml
+++ b/extender/src/test/resources/org/eclipse/gemini/blueprint/extender/internal/support/extender-custom-config.xml
@@ -24,6 +24,7 @@
<prop key="smth">bla</prop>
<prop key="dependencies.wait.time">200</prop>
<prop key="shutdown.wait.time">300</prop>
+ <prop key="shutdown.asynchronously">false</prop>
<prop key="process.annotations">true</prop>
</util:properties>
-</beans> \ No newline at end of file
+</beans>