Applied patch (FELIX-169) to fix how framework shutdown is handled, making
it more symmetric to system start up.


git-svn-id: https://svn.apache.org/repos/asf/incubator/felix/trunk@472017 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/Felix.java b/framework/src/main/java/org/apache/felix/framework/Felix.java
index 9860cbc..07f8c2c 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -341,8 +341,7 @@
             // Create a simple bundle info for the system bundle.
             BundleInfo info = new BundleInfo(
                 m_logger, new SystemBundleArchive(), null);
-            systembundle = new SystemBundle(this, info, activatorList,
-                m_secureAction);
+            systembundle = new SystemBundle(this, info, activatorList);
             // Create a module for the system bundle.
             IModuleDefinition md = new ModuleDefinition(
                 systembundle.getExports(), null, null, null);
@@ -522,6 +521,35 @@
         // The framework is now in its shutdown sequence.
         m_frameworkStatus = STOPPING_STATUS;
 
+        // Do the real shutdown work in a separate method to catch any problems
+        // without requiring a huge and ugly try-catch statement.
+        try
+        {
+            shutdownInternal();
+        }
+        catch (Exception ex)
+        {
+            fireFrameworkEvent(FrameworkEvent.ERROR, getBundle(0), ex);
+            m_logger.log(Logger.LOG_ERROR, "Error stopping framework.", ex);
+        }
+        
+        // Finally shutdown the JVM if the framework is running stand-alone.
+        String embedded = m_config.get(FelixConstants.EMBEDDED_EXECUTION_PROP);
+        boolean isEmbedded = (embedded == null) ? false : embedded.equals("true");
+        if (!isEmbedded)
+        {
+            m_secureAction.exit(0);
+        }
+    }
+    
+    /**
+     * This method actually performs the real shutdown operations of the
+     * framework in terms of setting the start level to zero, really stopping
+     * the system bundle, cleaning up any bundle remains and shutting down event
+     * dispatching.
+     */
+    private void shutdownInternal()
+    {
         // Use the start level service to set the start level to zero
         // in order to stop all bundles in the framework. Since framework
         // shutdown happens on its own thread, we can wait for the start
@@ -542,9 +570,12 @@
         // Just like initialize() called the system bundle's start()
         // method, we must call its stop() method here so that it
         // can perform any necessary clean up.
+        // Actually, the stop() method just asynchronously would call
+        // this method through shutdown(), hence we call the real SystemBundle
+        // shutdown method.
         try
         {
-            getBundle(0).stop();
+            ((SystemBundle) getBundle(0)).shutdown();
         }
         catch (Exception ex)
         {
diff --git a/framework/src/main/java/org/apache/felix/framework/SystemBundle.java b/framework/src/main/java/org/apache/felix/framework/SystemBundle.java
index b19a6c5..bcde5cb 100644
--- a/framework/src/main/java/org/apache/felix/framework/SystemBundle.java
+++ b/framework/src/main/java/org/apache/felix/framework/SystemBundle.java
@@ -34,15 +34,11 @@
     private Thread m_shutdownThread = null;
     private R4Export[] m_exports = null;
     private IContentLoader m_contentLoader = null;
-    private SecureAction m_secureAction = null;
 
-    protected SystemBundle(Felix felix, BundleInfo info, List activatorList,
-        SecureAction secureAction) throws BundleException
+    protected SystemBundle(Felix felix, BundleInfo info, List activatorList)
     {
         super(felix, info);
 
-        m_secureAction = secureAction;
-
         // Create an activator list if necessary.
         if (activatorList == null)
         {
@@ -162,7 +158,13 @@
         // This will be done after the framework is initialized.
     }
 
-    public synchronized void stop() throws BundleException
+    /**
+     * According to the spec, this method asynchronously stops the framework.
+     * To prevent multiple creations of useless separate threads in case of
+     * multiple calls to this method, the shutdown thread is only started if
+     * the framework is still in running state.
+     */
+    public synchronized void stop()
     {
         Object sm = System.getSecurityManager();
 
@@ -190,35 +192,11 @@
                             Logger.LOG_ERROR,
                             "SystemBundle: Error while shutting down.", ex);
                     }
-
-                    // Only shutdown the JVM if the framework is running stand-alone.
-                    String embedded = getFelix().getConfig()
-                        .get(FelixConstants.EMBEDDED_EXECUTION_PROP);
-                    boolean isEmbedded = (embedded == null)
-                        ? false : embedded.equals("true");
-                    if (!isEmbedded)
-                    {
-                        m_secureAction.exit(0);
-                    }
                 }
             };
             getInfo().setState(Bundle.STOPPING);
             m_shutdownThread.start();
         }
-        else if ((getFelix().getStatus() == Felix.STOPPING_STATUS) &&
-                 (Thread.currentThread() == m_shutdownThread))
-        {
-            // Callback from shutdown thread, so do our own stop.
-            try
-            {
-                getActivator().stop(getInfo().getContext());
-            }
-            catch (Throwable throwable)
-            {
-                throw new BundleException(
-                        "Unable to stop system bundle.", throwable);
-            }
-        }
     }
 
     public synchronized void uninstall() throws BundleException
@@ -253,4 +231,26 @@
         }
         return m_activator;
     }
-}
+    
+    /**
+     * Actually shuts down the system bundle. This method does what actually
+     * the {@link #stop()} method would do for regular bundles. Since the system
+     * bundle has to shutdown the framework, a separate method is used to stop
+     * the system bundle during framework shutdown.
+     * 
+     * @throws BundleException If an error occurrs stopping the system bundle
+     *      and any activators "started" on framework start time.
+     */
+    void shutdown() throws BundleException
+    {
+        // Callback from shutdown thread, so do our own stop.
+        try
+        {
+            getActivator().stop(getInfo().getContext());
+        }
+        catch (Throwable throwable)
+        {
+            throw new BundleException( "Unable to stop system bundle.", throwable );
+        }
+    }
+}
\ No newline at end of file