You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by gn...@apache.org on 2011/01/06 18:26:58 UTC

svn commit: r1055949 - in /karaf/trunk: main/ main/src/main/java/org/apache/karaf/main/ main/src/test/java/org/apache/karaf/main/ shell/wrapper/src/main/java/org/apache/karaf/shell/wrapper/

Author: gnodet
Date: Thu Jan  6 17:26:57 2011
New Revision: 1055949

URL: http://svn.apache.org/viewvc?rev=1055949&view=rev
Log:
[KARAF-327] Clean up graceful shutdown, make sure the wrapper is informed if the framework is exited through osgi:shutdown

Added:
    karaf/trunk/main/src/test/java/org/apache/karaf/main/TimeoutShutdownActivator.java
      - copied, changed from r1055859, karaf/trunk/main/src/main/java/org/apache/karaf/main/ShutdownCallback.java
Modified:
    karaf/trunk/main/pom.xml
    karaf/trunk/main/src/main/java/org/apache/karaf/main/Main.java
    karaf/trunk/main/src/main/java/org/apache/karaf/main/ShutdownCallback.java
    karaf/trunk/main/src/test/java/org/apache/karaf/main/MainStartTest.java
    karaf/trunk/shell/wrapper/src/main/java/org/apache/karaf/shell/wrapper/Main.java

Modified: karaf/trunk/main/pom.xml
URL: http://svn.apache.org/viewvc/karaf/trunk/main/pom.xml?rev=1055949&r1=1055948&r2=1055949&view=diff
==============================================================================
--- karaf/trunk/main/pom.xml (original)
+++ karaf/trunk/main/pom.xml Thu Jan  6 17:26:57 2011
@@ -51,6 +51,16 @@
             <artifactId>org.apache.servicemix.bundles.junit</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.swissbox</groupId>
+            <artifactId>pax-swissbox-tinybundles</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-logging</groupId>
+            <artifactId>commons-logging</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
     <build>
         <plugins>

Modified: karaf/trunk/main/src/main/java/org/apache/karaf/main/Main.java
URL: http://svn.apache.org/viewvc/karaf/trunk/main/src/main/java/org/apache/karaf/main/Main.java?rev=1055949&r1=1055948&r2=1055949&view=diff
==============================================================================
--- karaf/trunk/main/src/main/java/org/apache/karaf/main/Main.java (original)
+++ karaf/trunk/main/src/main/java/org/apache/karaf/main/Main.java Thu Jan  6 17:26:57 2011
@@ -30,7 +30,15 @@ import java.net.URLClassLoader;
 import java.security.AccessControlException;
 import java.security.Provider;
 import java.security.Security;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Random;
+import java.util.StringTokenizer;
+import java.util.TreeMap;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.regex.Matcher;
@@ -38,7 +46,6 @@ import java.util.regex.Pattern;
 
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleException;
 import org.osgi.framework.Constants;
 import org.osgi.framework.FrameworkEvent;
 import org.osgi.framework.ServiceReference;
@@ -147,6 +154,8 @@ public class Main {
     
     public static final String KARAF_FRAMEWORK = "karaf.framework";
 
+    public static final String KARAF_SHUTDOWN_TIMEOUT = "karaf.shutdown.timeout";
+
     public static final String KARAF_SHUTDOWN_PORT = "karaf.shutdown.port";
 
     public static final String KARAF_SHUTDOWN_HOST = "karaf.shutdown.host";
@@ -177,12 +186,18 @@ public class Main {
     private int defaultStartLevel = 100;
     private int lockStartLevel = 1;
     private int lockDelay = 1000;
+    private int shutdownTimeout = 5 * 60 * 1000;
     private boolean exiting = false;
+    private ShutdownCallback shutdownCallback;
 
     public Main(String[] args) {
         this.args = args;
     }
 
+    public void setShutdownCallback(ShutdownCallback shutdownCallback) {
+        this.shutdownCallback = shutdownCallback;
+    }
+
     public void launch() throws Exception {
         karafHome = Utils.getKarafHome();
         karafBase = Utils.getKarafDirectory(Main.PROP_KARAF_BASE, Main.ENV_KARAF_BASE, karafHome, false, true);
@@ -229,6 +244,7 @@ public class Main {
         lockStartLevel = Integer.parseInt(configProps.getProperty(PROPERTY_LOCK_LEVEL, Integer.toString(lockStartLevel)));
         lockDelay = Integer.parseInt(configProps.getProperty(PROPERTY_LOCK_DELAY, Integer.toString(lockDelay)));
         configProps.setProperty(Constants.FRAMEWORK_BEGINNING_STARTLEVEL, Integer.toString(lockStartLevel));
+        shutdownTimeout = Integer.parseInt(configProps.getProperty(KARAF_SHUTDOWN_TIMEOUT, Integer.toString(shutdownTimeout)));
         // Start up the OSGI framework
 
         InputStream is = classLoader.getResourceAsStream("META-INF/services/" + FrameworkFactory.class.getName());
@@ -247,52 +263,48 @@ public class Main {
         }.start();
     }
 
-    public void destroy(boolean await) throws Exception {
-		destroy(await, 0, null);
-	}
+    public void awaitShutdown() throws Exception {
+        while (true) {
+            FrameworkEvent event = framework.waitForStop(0);
+            if (event.getType() != FrameworkEvent.STOPPED_UPDATE) {
+                return;
+            }
+        }
+    }
 
-	public void destroy(boolean await, int timeout, ShutdownCallback callback) throws Exception {
+	public boolean destroy() throws Exception {
         if (framework == null) {
-            return;
+            return true;
         }
         try {
-            if (await) {
-                while (true) {
-                	FrameworkEvent event;
-	                if (callback != null) {
-	                	callback.waitingForShutdown();
-	                	event = framework.waitForStop(timeout);
-	                	//do the stoping in an extra thread
-	                	Runnable stopper = new Runnable() {
-							
-							public void run() {
-								try {
-									
-									framework.stop();
-								} catch (BundleException e) {
-									System.err.println("Exception while stoping framework: " + e);
-								}
-							}
-						};
-	                	Thread t = new Thread(stopper);
-	                	t.start();
-	                	while (t.getState() != Thread.State.TERMINATED && event.getType() == FrameworkEvent.WAIT_TIMEDOUT) {
-	                		callback.waitingForShutdown();
-	                		event = framework.waitForStop(timeout);
-	                	} 
-	                	break;
-                	} else {
-                		event = framework.waitForStop(0);
-                	}
-                	if (event.getType() != FrameworkEvent.STOPPED_UPDATE) {
-                        break;
-                    }
-                }
+            int step = 5000;
+
+            // Notify the callback asap
+            if (shutdownCallback != null) {
+                shutdownCallback.waitingForShutdown(step);
             }
+
+            // Stop the framework in case it's still active
             exiting = true;
             if (framework.getState() == Bundle.ACTIVE) {
                 framework.stop();
             }
+
+            int timeout = shutdownTimeout;
+            if (shutdownTimeout <= 0) {
+                timeout = Integer.MAX_VALUE;
+            }
+            while (timeout > 0) {
+                timeout -= step;
+                if (shutdownCallback != null) {
+                    shutdownCallback.waitingForShutdown(step * 2);
+                }
+                FrameworkEvent event = framework.waitForStop(step);
+                if (event.getType() != FrameworkEvent.WAIT_TIMEDOUT) {
+                    return true;
+                }
+            }
+            return false;
         } finally {
             unlock();
         }
@@ -394,8 +406,17 @@ public class Main {
                 ex.printStackTrace();
             }
             try {
-                main.destroy(true);
+                main.awaitShutdown();
+                boolean stopped = main.destroy();
                 restart = Boolean.getBoolean("karaf.restart");
+                if (!stopped) {
+                    if (restart) {
+                        System.err.println("Timeout waiting for framework to stop.  Restarting now.");
+                    } else {
+                        System.err.println("Timeout waiting for framework to stop.  Exiting VM.");
+                        main.setExitCode(-3);
+                    }
+                }
             } catch (Throwable ex) {
                 main.setExitCode(-2);
                 System.err.println("Error occured shutting down framework: " + ex);

Modified: karaf/trunk/main/src/main/java/org/apache/karaf/main/ShutdownCallback.java
URL: http://svn.apache.org/viewvc/karaf/trunk/main/src/main/java/org/apache/karaf/main/ShutdownCallback.java?rev=1055949&r1=1055948&r2=1055949&view=diff
==============================================================================
--- karaf/trunk/main/src/main/java/org/apache/karaf/main/ShutdownCallback.java (original)
+++ karaf/trunk/main/src/main/java/org/apache/karaf/main/ShutdownCallback.java Thu Jan  6 17:26:57 2011
@@ -32,6 +32,6 @@ public interface ShutdownCallback {
 	 * The callback method invoked to inform anyone listening that the 
 	 * Main class is still waiting for the completion of the shutdown. 
 	 */
-	void waitingForShutdown();
-	
+	void waitingForShutdown(int delay);
+
 }

Modified: karaf/trunk/main/src/test/java/org/apache/karaf/main/MainStartTest.java
URL: http://svn.apache.org/viewvc/karaf/trunk/main/src/test/java/org/apache/karaf/main/MainStartTest.java?rev=1055949&r1=1055948&r2=1055949&view=diff
==============================================================================
--- karaf/trunk/main/src/test/java/org/apache/karaf/main/MainStartTest.java (original)
+++ karaf/trunk/main/src/test/java/org/apache/karaf/main/MainStartTest.java Thu Jan  6 17:26:57 2011
@@ -23,9 +23,13 @@ import java.io.File;
 import junit.framework.Assert;
 import org.junit.Ignore;
 import org.junit.Test;
+import org.ops4j.pax.swissbox.tinybundles.core.TinyBundles;
 import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
 import org.osgi.framework.launch.Framework;
 
+import static org.ops4j.pax.swissbox.tinybundles.core.TinyBundles.withBnd;
+
 public class MainStartTest {
 
 	@Test
@@ -56,6 +60,38 @@ public class MainStartTest {
 		Assert.assertEquals(mvnUrl, bundles[2].getLocation());
 		Assert.assertEquals(Bundle.ACTIVE, bundles[1].getState());
 		Assert.assertEquals(Bundle.ACTIVE, bundles[2].getState());
-		main.destroy(false);
+		main.destroy();
 	}
+
+    @Test
+    public void testStopWithTimeout() throws Exception {
+        File basedir = new File(getClass().getClassLoader().getResource("foo").getPath()).getParentFile();
+        File home = new File(basedir, "test-karaf-home");
+        File data = new File(home, "data");
+
+        Utils.deleteDirectory(data);
+
+		String[] args = new String[0];
+		System.setProperty("karaf.home", home.toString());
+		System.setProperty("karaf.data", data.toString());
+
+        Main main = new Main(args);
+        main.launch();
+        Thread.sleep(1000);
+        Framework framework = main.getFramework();
+        String activatorName = TimeoutShutdownActivator.class.getName().replace('.', '/') + ".class";
+        Bundle bundle = framework.getBundleContext().installBundle("foo",
+                TinyBundles.newBundle()
+                    .set( Constants.BUNDLE_ACTIVATOR, TimeoutShutdownActivator.class.getName() )
+                    .add( activatorName, getClass().getClassLoader().getResourceAsStream( activatorName ) )
+                    .build( withBnd() )
+        );
+        bundle.start();
+
+        long t0 = System.currentTimeMillis();
+        main.destroy();
+        long t1 = System.currentTimeMillis();
+//        System.err.println("Shutdown duration: " + (t1 - t0) + " ms");
+        Assert.assertTrue((t1 - t0) > TimeoutShutdownActivator.TIMEOUT / 2);
+    }
 }

Copied: karaf/trunk/main/src/test/java/org/apache/karaf/main/TimeoutShutdownActivator.java (from r1055859, karaf/trunk/main/src/main/java/org/apache/karaf/main/ShutdownCallback.java)
URL: http://svn.apache.org/viewvc/karaf/trunk/main/src/test/java/org/apache/karaf/main/TimeoutShutdownActivator.java?p2=karaf/trunk/main/src/test/java/org/apache/karaf/main/TimeoutShutdownActivator.java&p1=karaf/trunk/main/src/main/java/org/apache/karaf/main/ShutdownCallback.java&r1=1055859&r2=1055949&rev=1055949&view=diff
==============================================================================
--- karaf/trunk/main/src/main/java/org/apache/karaf/main/ShutdownCallback.java (original)
+++ karaf/trunk/main/src/test/java/org/apache/karaf/main/TimeoutShutdownActivator.java Thu Jan  6 17:26:57 2011
@@ -1,37 +1,34 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.karaf.main;
-
-
-/**
- * <p>
- * This interface is a callback interface for the stoping process. 
- * It's main purpose is to give the ServiceWrapper a way of waiting 
- * for the Framework to gracefully stop the Server. 
- * <p>
- */
-public interface ShutdownCallback {
-
-	/**
-	 * The callback method invoked to inform anyone listening that the 
-	 * Main class is still waiting for the completion of the shutdown. 
-	 */
-	void waitingForShutdown();
-	
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.karaf.main;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+public class TimeoutShutdownActivator implements BundleActivator {
+
+    public static int TIMEOUT = 10000;
+
+    public void start(BundleContext context) throws Exception {
+    }
+
+    public void stop(BundleContext context) throws Exception {
+        Thread.sleep(TIMEOUT);
+    }
+}

Modified: karaf/trunk/shell/wrapper/src/main/java/org/apache/karaf/shell/wrapper/Main.java
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/wrapper/src/main/java/org/apache/karaf/shell/wrapper/Main.java?rev=1055949&r1=1055948&r2=1055949&view=diff
==============================================================================
--- karaf/trunk/shell/wrapper/src/main/java/org/apache/karaf/shell/wrapper/Main.java (original)
+++ karaf/trunk/shell/wrapper/src/main/java/org/apache/karaf/shell/wrapper/Main.java Thu Jan  6 17:26:57 2011
@@ -23,11 +23,10 @@ import org.tanukisoftware.wrapper.Wrappe
 /**
  * Java Service Wrapper Main class
  */
-public class Main implements WrapperListener, ShutdownCallback {
+public class Main extends Thread implements WrapperListener, ShutdownCallback {
 
-    private static final int TIMEOUT = 1000; //wainting timeout for a second should be enough
-	private static final int TIMEOUT_OFFSET = 500; // the offset for the wrapper, to leave us some time to breath
-	private org.apache.karaf.main.Main main;
+   	private org.apache.karaf.main.Main main;
+    private volatile boolean destroying;
 
     /*---------------------------------------------------------------
      * Constructors
@@ -57,6 +56,8 @@ public class Main implements WrapperList
         try
         {
             main.launch();
+            main.setShutdownCallback(this);
+            start();
             return null;
         }
         catch (Throwable ex)
@@ -65,7 +66,17 @@ public class Main implements WrapperList
             ex.printStackTrace();
             return -1;
         }
+    }
 
+    public void run() {
+        try {
+            main.awaitShutdown();
+            if (!destroying) {
+                WrapperManager.stop(main.getExitCode());
+            }
+        } catch (Exception e) {
+            // Ignore
+        }
     }
 
     /**
@@ -88,7 +99,8 @@ public class Main implements WrapperList
     {
         try
         {
-            main.destroy(true, TIMEOUT, this);
+            destroying = true;
+            main.destroy();
         }
         catch (Throwable ex)
         {
@@ -97,15 +109,15 @@ public class Main implements WrapperList
             return -2;
         }
 
-        return exitCode;
+        return main.getExitCode();
     }
     
     /**
      * Call-back method is called by the @{link org.apache.karaf.main.Main} for Signaling 
      * that the stopping process is in progress and the wrapper doesn't kill the JVM.  
      */
-    public void waitingForShutdown() {
-    	WrapperManager.signalStopping(TIMEOUT + TIMEOUT_OFFSET);
+    public void waitingForShutdown(int delay) {
+    	WrapperManager.signalStopping(delay);
     }
 
     /**