Tried to improve Felix' shutdown sequence, which was confusing and perhaps
not altogether correct. Now all shutdowns occur via calling stop() on the
system bundle and Felix stops the system bundle now in a similar fashion
as normal bundles to ensure that proper events are fired.


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@543242 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleImpl.java b/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
index 69e89ce..6c7da07 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
@@ -54,7 +54,7 @@
 
     public BundleContext getBundleContext()
     {
-        return m_info.getContext();
+        return m_info.getBundleContext();
     }
 
     public long getBundleId()
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleInfo.java b/framework/src/main/java/org/apache/felix/framework/BundleInfo.java
index 274b69f..8697012 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleInfo.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleInfo.java
@@ -450,12 +450,12 @@
         }
     }
 
-    public BundleContext getContext()
+    public BundleContext getBundleContext()
     {
         return m_context;
     }
 
-    public void setContext(BundleContext context)
+    public void setBundleContext(BundleContext context)
     {
         m_context = context;
     }
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 b6b7321..ce90144 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -395,7 +395,7 @@
 
         // Now that the system bundle is successfully created we can give
         // its bundle context to the logger so that it can track log services.
-        m_logger.setSystemBundleContext(systembundle.getInfo().getContext());
+        m_logger.setSystemBundleContext(systembundle.getInfo().getBundleContext());
 
         // Now reload the cached bundles.
         BundleArchive[] archives = null;
@@ -523,36 +523,41 @@
      * This method cleanly shuts down the framework, it must be called at the
      * end of a session in order to shutdown all active bundles.
     **/
-    public synchronized void shutdown()
+    public void shutdown()
     {
-        // Change framework status from running to stopping.
-        // If framework is not running, then just return.
-        if (m_frameworkStatus != RUNNING_STATUS)
+        // Shut the framework down by calling stop() on the system bundle.
+        // Since stop() on the system bundle will return immediately, we
+        // will synchronize on the framework instance so we can use it to
+        // be notified when the shutdown is complete.
+        synchronized (this)
         {
-            return;
-        }
+            if (m_frameworkStatus == RUNNING_STATUS)
+            {
+                try
+                {
+                    getBundle(0).stop();
+                }
+                catch (BundleException ex)
+                {
+                    fireFrameworkEvent(FrameworkEvent.ERROR, getBundle(0), ex);
+                    m_logger.log(
+                        Logger.LOG_ERROR,
+                        "Error stopping system bundle.",
+                        ex);
+                }
+            }
 
-        // 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);
+            while (m_frameworkStatus != INITIAL_STATUS)
+            {
+                try
+                {
+                    wait();
+                }
+                catch (InterruptedException ex)
+                {
+                    // Keep waiting if necessary.
+                }
+            }
         }
     }
 
@@ -562,8 +567,21 @@
      * the system bundle, cleaning up any bundle remains and shutting down event
      * dispatching.
      */
-    private void shutdownInternal()
+    void shutdownInternal()
     {
+        synchronized (this)
+        {
+            // Change framework status from running to stopping.
+            // If framework is not running, then just return.
+            if (m_frameworkStatus != RUNNING_STATUS)
+            {
+                return;
+            }
+
+            // The framework is now in its shutdown sequence.
+            m_frameworkStatus = STOPPING_STATUS;
+        }
+
         // 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
@@ -581,22 +599,6 @@
             // Should never happen.
         }
 
-        // 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
-        {
-            ((SystemBundle) getBundle(0)).shutdown();
-        }
-        catch (Exception ex)
-        {
-            fireFrameworkEvent(FrameworkEvent.ERROR, getBundle(0), ex);
-            m_logger.log(Logger.LOG_ERROR, "Error stopping system bundle.", ex);
-        }
-
         // Since they may be updated and uninstalled bundles that
         // have not been refreshed, we will take care of refreshing
         // them during shutdown.
@@ -643,28 +645,40 @@
         // Shutdown event dispatching queue.
         EventDispatcher.shutdown();
 
-        // The framework is no longer in a usable state.
-        m_frameworkStatus = INITIAL_STATUS;
-
         // Remove all bundles from the module factory so that any
         // open resources will be closed.
         bundles = getBundles();
         for (int i = 0; i < bundles.length; i++)
         {
             BundleImpl bundle = (BundleImpl) bundles[i];
-            try
+            IModule[] modules = bundle.getInfo().getModules();
+            for (int j = 0; j < modules.length; j++)
             {
-                IModule[] modules = bundle.getInfo().getModules();
-                for (int j = 0; j < modules.length; j++)
+                try
                 {
                     m_factory.removeModule(modules[j]);
                 }
+                catch (Exception ex)
+                {
+                    m_logger.log(Logger.LOG_ERROR,
+                       "Unable to clean up " + bundle.getInfo().getLocation(), ex);
+                }
             }
-            catch (Exception ex)
-            {
-                m_logger.log(Logger.LOG_ERROR,
-                   "Unable to clean up " + bundle.getInfo().getLocation(), ex);
-            }
+        }
+
+        // Notify any waiters that the framework is back in its initial state.
+        synchronized (this)
+        {
+            m_frameworkStatus = INITIAL_STATUS;
+            notifyAll();
+        }
+
+        // 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);
         }
     }
 
@@ -1281,7 +1295,7 @@
         try
         {
             // Set the bundle's context.
-            info.setContext(new BundleContextImpl(this, bundle));
+            info.setBundleContext(new BundleContextImpl(this, bundle));
 
             // Set the bundle's activator.
             info.setActivator(createBundleActivator(bundle.getInfo()));
@@ -1289,8 +1303,7 @@
             // Activate the bundle if it has an activator.
             if (bundle.getInfo().getActivator() != null)
             {
-                m_secureAction.startActivator(info.getActivator(),
-                    info.getContext());
+                m_secureAction.startActivator(info.getActivator(),info.getBundleContext());
             }
 
             // TODO: CONCURRENCY - Reconsider firing event outside of the
@@ -1305,8 +1318,8 @@
             info.setState(Bundle.RESOLVED);
 
             // Clean up the bundle context.
-            ((BundleContextImpl) info.getContext()).invalidate();
-            info.setContext(null);
+            ((BundleContextImpl) info.getBundleContext()).invalidate();
+            info.setBundleContext(null);
 
             // Unregister any services offered by this bundle.
             m_registry.unregisterServices(bundle);
@@ -1667,8 +1680,7 @@
         {
             if (bundle.getInfo().getActivator() != null)
             {
-                m_secureAction.stopActivator(info.getActivator(),
-                    info.getContext());
+                m_secureAction.stopActivator(info.getActivator(),info.getBundleContext());
             }
 
             // Try to save the activator in the cache.
@@ -1696,22 +1708,27 @@
             rethrow = th;
         }
 
-        // Clean up the bundle context.
-        ((BundleContextImpl) info.getContext()).invalidate();
-        info.setContext(null);
+        // Do not clean up after the system bundle since it will
+        // clean up after itself.
+        if (info.getBundleId() != 0)
+        {
+            // Clean up the bundle context.
+            ((BundleContextImpl) info.getBundleContext()).invalidate();
+            info.setBundleContext(null);
 
-        // Unregister any services offered by this bundle.
-        m_registry.unregisterServices(bundle);
+            // Unregister any services offered by this bundle.
+            m_registry.unregisterServices(bundle);
 
-        // Release any services being used by this bundle.
-        m_registry.ungetServices(bundle);
+            // Release any services being used by this bundle.
+            m_registry.ungetServices(bundle);
 
-        // The spec says that we must remove all event
-        // listeners for a bundle when it is stopped.
-        m_dispatcher.removeListeners(bundle);
+            // The spec says that we must remove all event
+            // listeners for a bundle when it is stopped.
+            m_dispatcher.removeListeners(bundle);
 
-        info.setState(Bundle.RESOLVED);
-        fireBundleEvent(BundleEvent.STOPPED, bundle);
+            info.setState(Bundle.RESOLVED);
+            fireBundleEvent(BundleEvent.STOPPED, bundle);
+        }
 
         // Throw activator error if there was one.
         if (rethrow != null)
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 4884019..eef8cea 100644
--- a/framework/src/main/java/org/apache/felix/framework/SystemBundle.java
+++ b/framework/src/main/java/org/apache/felix/framework/SystemBundle.java
@@ -36,11 +36,11 @@
 class SystemBundle extends BundleImpl implements IModuleDefinition, PrivilegedAction
 {
     private List m_activatorList = null;
-    private SystemBundleActivator m_activator = null;
-    private Thread m_shutdownThread = null;
-    private ICapability[] m_exports = null;
+    private Map m_activatorContextMap = null;
     private IContentLoader m_contentLoader = null;
+    private ICapability[] m_exports = null;
     private Set m_exportNames = null;
+    private Thread m_shutdownThread = null;
 
     protected SystemBundle(Felix felix, BundleInfo info, List activatorList)
     {
@@ -63,6 +63,8 @@
 
         m_activatorList = activatorList;
 
+        info.setActivator(new SystemBundleActivator());
+        
         // The system bundle exports framework packages as well as
         // arbitrary user-defined packages from the system class path.
         // We must construct the system bundle's export metadata.
@@ -149,8 +151,8 @@
 
         try
         {
-            getInfo().setContext(new BundleContextImpl(getFelix(), this));
-            getActivator().start(getInfo().getContext());
+            getInfo().setBundleContext(new BundleContextImpl(getFelix(), this));
+            getInfo().getActivator().start(getInfo().getBundleContext());
         }
         catch (Throwable throwable)
         {
@@ -163,45 +165,9 @@
         // This will be done after the framework is initialized.
     }
 
-    /**
-     * 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 void stop()
+    public void stop() throws BundleException
     {
-        Object sm = System.getSecurityManager();
-
-        if (sm != null)
-        {
-            ((SecurityManager) sm).checkPermission(new AdminPermission(this,
-                AdminPermission.EXECUTE));
-        }
-
-        // Spec says stop() on SystemBundle should return immediately and
-        // shutdown framework on another thread.
-        if (getFelix().getStatus() == Felix.RUNNING_STATUS)
-        {
-            // Initial call of stop, so kick off shutdown.
-            m_shutdownThread = new Thread("FelixShutdown") {
-                public void run()
-                {
-                    try
-                    {
-                        getFelix().shutdown();
-                    }
-                    catch (Exception ex)
-                    {
-                        getFelix().getLogger().log(
-                            Logger.LOG_ERROR,
-                            "SystemBundle: Error while shutting down.", ex);
-                    }
-                }
-            };
-            getInfo().setState(Bundle.STOPPING);
-            m_shutdownThread.start();
-        }
+        super.stop();
     }
 
     public void uninstall() throws BundleException
@@ -228,43 +194,6 @@
         throw new BundleException("System bundle update not implemented yet.");
     }
 
-    protected BundleActivator getActivator()
-    {
-        if (m_activator == null)
-        {
-            m_activator = new SystemBundleActivator(m_activatorList);
-        }
-        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
-        {
-            getFelix().m_secureAction.stopActivator(getActivator(),
-                getInfo().getContext());
-        }
-        catch (Throwable throwable)
-        {
-            throw new BundleException( "Unable to stop system bundle.", throwable );
-        }
-    }
-
-    boolean exports(String packageName)
-    {
-        return m_exportNames.contains(packageName);
-    }
-
     public ICapability[] getCapabilities()
     {
         return m_exports;
@@ -308,29 +237,6 @@
         }
     }
     
-    void startExtensionBundle(BundleImpl bundle) 
-    {
-        String activatorClass = (String)
-        bundle.getInfo().getCurrentHeader().get(
-            FelixConstants.FELIX_EXTENSION_ACTIVATOR);
-        
-        if (activatorClass != null)
-        {
-            try
-            {
-                m_activator.addActivator(((BundleActivator)
-                    getClass().getClassLoader().loadClass(
-                    activatorClass.trim()).newInstance()),
-                    new BundleContextImpl(getFelix(), bundle));
-            }
-            catch (Throwable ex)
-            {
-                getFelix().getLogger().log(Logger.LOG_WARNING,
-                    "Unable to start Felix Extension Activator", ex);
-            }
-        }
-    }
-
     public Object run()
     {
         _addExtensionBundle((BundleImpl) m_tempBundle.get());
@@ -391,6 +297,109 @@
         systemArchive.setManifestHeader(headers);
     }
 
+    void startExtensionBundle(BundleImpl bundle)
+    {
+        String activatorClass = (String)
+        bundle.getInfo().getCurrentHeader().get(
+            FelixConstants.FELIX_EXTENSION_ACTIVATOR);
+        
+        if (activatorClass != null)
+        {
+            try
+            {
+                BundleActivator activator = (BundleActivator)
+                    getClass().getClassLoader().loadClass(
+                        activatorClass.trim()).newInstance();
+                m_activatorList.add(activator);
+                if (m_activatorContextMap == null)
+                {
+                    m_activatorContextMap = new HashMap();
+                }
+                BundleContext context = new BundleContextImpl(getFelix(), bundle);
+                m_activatorContextMap.put(activator, context);
+                activator.start(context);
+            }
+            catch (Throwable ex)
+            {
+                getFelix().getLogger().log(Logger.LOG_WARNING,
+                    "Unable to start Felix Extension Activator", ex);
+            }
+        }
+    }
+
+    private class SystemBundleActivator implements BundleActivator, Runnable
+    {
+        public void start(BundleContext context) throws Exception
+        {
+            getInfo().setBundleContext(context);
+
+            // Start all activators.
+            for (int i = 0; i < m_activatorList.size(); i++)
+            {
+                ((BundleActivator) m_activatorList.get(i)).start(context);
+            }
+        }
+
+        public void stop(BundleContext context) throws Exception
+        {
+            getInfo().setBundleContext(context);
+
+            // Spec says stop() on SystemBundle should return immediately and
+            // shutdown framework on another thread.
+            if (getFelix().getStatus() == Felix.RUNNING_STATUS)
+            {
+                // Initial call of stop, so kick off shutdown.
+                m_shutdownThread = new Thread(this, "FelixShutdown");
+                m_shutdownThread.start();
+            }
+        }
+
+        public void run()
+        {
+            // First, stop all other bundles.
+            try
+            {
+                getFelix().shutdownInternal();
+            }
+            catch (Exception ex)
+            {
+                getFelix().getLogger().log(
+                    Logger.LOG_ERROR,
+                    "SystemBundle: Error while shutting down.", ex);
+            }
+
+            // Next, stop all system bundle activators.
+            if (m_activatorList != null)
+            {
+                // Stop all activators.
+                for (int i = 0; i < m_activatorList.size(); i++)
+                {
+                    try
+                    {
+                        if ((m_activatorContextMap != null) &&
+                            m_activatorContextMap.containsKey(m_activatorList.get(i)))
+                        {
+                            ((BundleActivator) m_activatorList.get(i)).stop(
+                                (BundleContext) m_activatorContextMap.get(
+                                m_activatorList.get(i)));
+                        }
+                        else
+                        {
+                            ((BundleActivator) m_activatorList.get(i)).stop(getInfo().getBundleContext());
+                        }
+                    }
+                    catch (Throwable throwable)
+                    {
+                        getFelix().getLogger().log(
+                            Logger.LOG_WARNING,
+                            "Exception stopping a system bundle activator.",
+                            throwable);
+                    }
+                }
+            }
+        }
+    }
+
     private class SystemBundleContentLoader implements IContentLoader
     {
         private ISearchPolicy m_searchPolicy = null;
diff --git a/framework/src/main/java/org/apache/felix/framework/SystemBundleActivator.java b/framework/src/main/java/org/apache/felix/framework/SystemBundleActivator.java
deleted file mode 100644
index dfb9c7e..0000000
--- a/framework/src/main/java/org/apache/felix/framework/SystemBundleActivator.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * 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.felix.framework;
-
-import java.util.*;
-
-import org.osgi.framework.BundleActivator;
-import org.osgi.framework.BundleContext;
-
-class SystemBundleActivator implements BundleActivator
-{
-    private List m_activatorList = null;
-    private BundleContext m_context = null;
-    private Map m_activatorContextMap = null;
-
-    SystemBundleActivator(List activatorList)
-    {
-        m_activatorList = activatorList;
-    }
-
-    public void start(BundleContext context) throws Exception
-    {
-        m_context = context;
-
-        // Start all activators.
-        if (m_activatorList != null)
-        {
-            for (int i = 0; i < m_activatorList.size(); i++)
-            {
-                ((BundleActivator) m_activatorList.get(i)).start(context);
-            }
-        }
-    }
-
-    public void stop(BundleContext context) throws Exception
-    {
-        if (m_activatorList != null)
-        {
-            // Stop all activators.
-            for (int i = 0; i < m_activatorList.size(); i++)
-            {
-                if ((m_activatorContextMap != null) &&
-                    m_activatorContextMap.containsKey(m_activatorList.get(i)))
-                {
-                    ((BundleActivator) m_activatorList.get(i)).stop(
-                        (BundleContext) m_activatorContextMap.get(
-                        m_activatorList.get(i)));
-                }
-                else
-                {
-                    ((BundleActivator) m_activatorList.get(i)).stop(context);
-                }
-            }
-        }
-    }
-
-    public BundleContext getBundleContext()
-    {
-        return m_context;
-    }
-
-    void addActivator(BundleActivator activator, BundleContext context) throws Exception
-    {
-        if (m_activatorList == null)
-        {
-            m_activatorList = new ArrayList();
-        }
-
-        m_activatorList.add(activator);
-
-        if (context != null)
-        {
-            if (m_activatorContextMap == null)
-            {
-                m_activatorContextMap = new HashMap();
-            }
-
-            m_activatorContextMap.put(activator, context);
-
-            activator.start(context);
-        }
-        else if (m_context != null)
-        {
-            activator.start(m_context);
-        }
-    }
-}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/URLHandlersContentHandlerProxy.java b/framework/src/main/java/org/apache/felix/framework/URLHandlersContentHandlerProxy.java
index a554508..f8c8a97 100644
--- a/framework/src/main/java/org/apache/felix/framework/URLHandlersContentHandlerProxy.java
+++ b/framework/src/main/java/org/apache/felix/framework/URLHandlersContentHandlerProxy.java
@@ -103,9 +103,7 @@
         {
             // Get the framework's system bundle context.
             BundleContext context =
-                ((SystemBundleActivator)
-                    ((SystemBundle) framework.getBundle(0)).getActivator())
-                        .getBundleContext();
+                ((BundleImpl) framework.getBundle(0)).getInfo().getBundleContext();
             // Create a filter for the mime type.
             String filter = 
                 "(&(objectClass="
diff --git a/framework/src/main/java/org/apache/felix/framework/URLHandlersStreamHandlerProxy.java b/framework/src/main/java/org/apache/felix/framework/URLHandlersStreamHandlerProxy.java
index 7657f00..61b87ca 100644
--- a/framework/src/main/java/org/apache/felix/framework/URLHandlersStreamHandlerProxy.java
+++ b/framework/src/main/java/org/apache/felix/framework/URLHandlersStreamHandlerProxy.java
@@ -203,9 +203,7 @@
         {
             // Get the framework's system bundle context.
             BundleContext context =
-                ((SystemBundleActivator)
-                    ((SystemBundle) framework.getBundle(0)).getActivator())
-                        .getBundleContext();
+                ((BundleImpl) framework.getBundle(0)).getInfo().getBundleContext();
             // Create a filter for the protocol.
             String filter = 
                 "(&(objectClass="