FELIX-2089 "unset" the bundle context on stop for the LogWrapper to not cause an IllegalStateException when logging after the bundle has already stopped


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@910976 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/Activator.java b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/Activator.java
index 7bb3906..39378ad 100644
--- a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/Activator.java
+++ b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/Activator.java
@@ -83,5 +83,8 @@
             m_config.destroy();
         }
         m_config = null;
+
+        // FELIX-2089: "unset" the bundle context on stop
+        LogWrapper.setContext(null);
     }
 }
diff --git a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/util/LogWrapper.java b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/util/LogWrapper.java
index c55d00d..85ab3b1 100644
--- a/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/util/LogWrapper.java
+++ b/eventadmin/impl/src/main/java/org/apache/felix/eventadmin/impl/util/LogWrapper.java
@@ -1,4 +1,4 @@
-/* 
+/*
  * 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
@@ -15,7 +15,7 @@
  * KIND, either express or implied.  See the License for the
  * specific language governing permissions and limitations
  * under the License.
- */  
+ */
 package org.apache.felix.eventadmin.impl.util;
 
 import java.util.HashSet;
@@ -32,50 +32,50 @@
 
 /**
  * This class mimics the standard OSGi <tt>LogService</tt> interface. An
- * instance of this class will be used by the EventAdmin for all logging. The 
+ * instance of this class will be used by the EventAdmin for all logging. The
  * implementation of this class sends log messages to standard output, if no
  * <tt>LogService</tt> is present; it uses a log service if one is
- * installed in the framework. To do that without creating a hard dependency on the 
+ * installed in the framework. To do that without creating a hard dependency on the
  * package it uses fully qualified class names and registers a listener with the
- * framework hence, it does not need access to the <tt>LogService</tt> class but will 
- * use it if the listener is informed about an available service. By using a 
+ * framework hence, it does not need access to the <tt>LogService</tt> class but will
+ * use it if the listener is informed about an available service. By using a
  * DynamicImport-Package dependency we don't need the package but
  * use it if present. Additionally, all log methods prefix the log message with
  * <tt>EventAdmin: </tt>.
- * 
+ *
  * @see org.osgi.service.log.LogService
- * 
+ *
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
 **/
-// TODO: At the moment we log a message to all currently available LogServices. 
+// TODO: At the moment we log a message to all currently available LogServices.
 //       Maybe, we should only log to the one with the highest ranking instead?
-//       What is the best practice in this case? 
+//       What is the best practice in this case?
 public class LogWrapper
 {
     /**
      * ERROR LEVEL
-     * 
-     * @see org.osgi.service.log.LogService#LOG_ERROR 
+     *
+     * @see org.osgi.service.log.LogService#LOG_ERROR
      */
     public static final int LOG_ERROR = 1;
 
     /**
      * WARNING LEVEL
-     * 
+     *
      * @see org.osgi.service.log.LogService#LOG_WARNING
      */
     public static final int LOG_WARNING = 2;
 
     /**
      * INFO LEVEL
-     * 
+     *
      * @see org.osgi.service.log.LogService#LOG_INFO
      */
     public static final int LOG_INFO = 3;
 
     /**
      * DEBUG LEVEL
-     * 
+     *
      * @see org.osgi.service.log.LogService#LOG_DEBUG
      */
     public static final int LOG_DEBUG = 4;
@@ -83,10 +83,11 @@
     // A set containing the currently available LogServices. Furthermore used as lock
     private final Set m_loggerRefs = new HashSet();
 
-    // Only null while not set and m_loggerRefs is empty hence, only needs to be 
+    // Only null while not set and m_loggerRefs is empty hence, only needs to be
     // checked in case m_loggerRefs is empty otherwise it will not be null.
     private BundleContext m_context;
 
+    private ServiceListener m_logServiceListener;
     /*
      * A thread save variant of the double checked locking singleton.
      */
@@ -94,12 +95,12 @@
     {
         static final LogWrapper m_singleton = new LogWrapper();
     }
-    
+
     /**
      * Returns the singleton instance of this LogWrapper that can be used to send
      * log messages to all currently available LogServices or to standard output,
      * respectively.
-     * 
+     *
      * @return the singleton instance of this LogWrapper.
      */
     public static LogWrapper getLogger()
@@ -109,49 +110,72 @@
 
     /**
      * Set the <tt>BundleContext</tt> of the bundle. This method registers a service
-     * listener for LogServices with the framework that are subsequently used to 
+     * listener for LogServices with the framework that are subsequently used to
      * log messages.
-     * 
+     * <p>
+     * If the bundle context is <code>null</code>, the service listener is
+     * unregistered and all remaining references to LogServices dropped before
+     * internally clearing the bundle context field.
+     *
      *  @param context The context of the bundle.
      */
-    public static void setContext(final BundleContext context)
+    public static void setContext( final BundleContext context )
     {
-        LogWrapperLoader.m_singleton.setBundleContext(context);
+        LogWrapper logWrapper = LogWrapperLoader.m_singleton;
 
-        try
+        // context is removed, unregister and drop references
+        if ( context == null )
         {
-            context.addServiceListener(new ServiceListener()
+            if ( logWrapper.m_logServiceListener != null )
             {
-                // Add a newly available LogService reference to the singleton.
-                public void serviceChanged(final ServiceEvent event)
+                logWrapper.m_context.removeServiceListener( logWrapper.m_logServiceListener );
+                logWrapper.m_logServiceListener = null;
+            }
+            logWrapper.removeLoggerRefs();
+        }
+
+        // set field
+        logWrapper.setBundleContext( context );
+
+        // context is set, register and get existing services
+        if ( context != null )
+        {
+            try
+            {
+                ServiceListener listener = new ServiceListener()
                 {
-                    if (ServiceEvent.REGISTERED == event.getType())
+                    // Add a newly available LogService reference to the singleton.
+                    public void serviceChanged( final ServiceEvent event )
                     {
-                        LogWrapperLoader.m_singleton.addLoggerRef(
-                            event.getServiceReference());
+                        if ( ServiceEvent.REGISTERED == event.getType() )
+                        {
+                            LogWrapperLoader.m_singleton.addLoggerRef( event.getServiceReference() );
+                        }
+                        // unregistered services are handled in the next log operation.
                     }
-                    // unregistered services are handled in the next log operation.
-                }
 
-            }, "(" + Constants.OBJECTCLASS
-                + "=org.osgi.service.log.LogService)");
+                };
+                context.addServiceListener( listener, "(" + Constants.OBJECTCLASS + "=org.osgi.service.log.LogService)" );
+                logWrapper.m_logServiceListener = listener;
 
-            // Add all available LogService references to the singleton.
-            final ServiceReference[] refs = context.getServiceReferences(
-                "org.osgi.service.log.LogService", null);
+                // Add all available LogService references to the singleton.
+                final ServiceReference[] refs = context.getServiceReferences( "org.osgi.service.log.LogService", null );
 
-            if (null != refs)
-            {
-                for (int i = 0; i < refs.length; i++)
+                if ( null != refs )
                 {
-                    LogWrapperLoader.m_singleton.addLoggerRef(refs[i]);
+                    for ( int i = 0; i < refs.length; i++ )
+                    {
+                        logWrapper.addLoggerRef( refs[i] );
+                    }
                 }
             }
-        } catch (InvalidSyntaxException e)
-        {
-            // this never happens
+            catch ( InvalidSyntaxException e )
+            {
+                // this never happens
+            }
         }
-    } 
+    }
+
 
     /*
      * The private singleton constructor.
@@ -162,9 +186,20 @@
     }
 
     /*
+     * Removes all references to LogServices still kept
+     */
+    void removeLoggerRefs()
+    {
+        synchronized ( m_loggerRefs )
+        {
+            m_loggerRefs.clear();
+        }
+    }
+
+    /*
      * Add a reference to a newly available LogService
      */
-    void addLoggerRef(final ServiceReference ref)
+    void addLoggerRef( final ServiceReference ref )
     {
         synchronized (m_loggerRefs)
         {
@@ -186,7 +221,7 @@
     /**
      * Log a message with the given log level. Note that this will prefix the message
      * with <tt>EventAdmin: </tt>.
-     * 
+     *
      * @param level The log level with which to log the msg.
      * @param msg The message to log.
      */
@@ -196,7 +231,7 @@
         synchronized(m_loggerRefs)
         {
             final String logMsg = "EventAdmin: " + msg;
-            
+
             if (!m_loggerRefs.isEmpty())
             {
                 // There is at least one LogService available hence, we can use the
@@ -204,19 +239,19 @@
                 for (Iterator iter = m_loggerRefs.iterator(); iter.hasNext();)
                 {
                     final ServiceReference next = (ServiceReference) iter.next();
-                    
-                    org.osgi.service.log.LogService logger = 
+
+                    org.osgi.service.log.LogService logger =
                         (org.osgi.service.log.LogService) m_context.getService(next);
-    
+
                     if (null != logger)
                     {
                         logger.log(level, logMsg);
-                        
+
                         m_context.ungetService(next);
                     }
                     else
                     {
-                        // The context returned null for the reference - it follows 
+                        // The context returned null for the reference - it follows
                         // that the service is unregistered and we can remove it
                         iter.remove();
                     }
@@ -230,9 +265,9 @@
     }
 
     /**
-     * Log a message with the given log level and the associated exception. Note that 
+     * Log a message with the given log level and the associated exception. Note that
      * this will prefix the message with <tt>EventAdmin: </tt>.
-     * 
+     *
      * @param level The log level with which to log the msg.
      * @param msg The message to log.
      * @param ex The exception associated with the message.
@@ -243,7 +278,7 @@
         synchronized(m_loggerRefs)
         {
             final String logMsg = "EventAdmin: " + msg;
-            
+
             if (!m_loggerRefs.isEmpty())
             {
                 // There is at least one LogService available hence, we can use the
@@ -251,19 +286,19 @@
                 for (Iterator iter = m_loggerRefs.iterator(); iter.hasNext();)
                 {
                     final ServiceReference next = (ServiceReference) iter.next();
-                    
-                    org.osgi.service.log.LogService logger = 
+
+                    org.osgi.service.log.LogService logger =
                         (org.osgi.service.log.LogService) m_context.getService(next);
-    
+
                     if (null != logger)
                     {
                         logger.log(level, logMsg, ex);
-                        
+
                         m_context.ungetService(next);
                     }
                     else
                     {
-                        // The context returned null for the reference - it follows 
+                        // The context returned null for the reference - it follows
                         // that the service is unregistered and we can remove it
                         iter.remove();
                     }
@@ -279,7 +314,7 @@
     /**
      * Log a message with the given log level together with the associated service
      * reference. Note that this will prefix the message with <tt>EventAdmin: </tt>.
-     * 
+     *
      * @param sr The reference of the service associated with this message.
      * @param level The log level with which to log the msg.
      * @param msg The message to log.
@@ -290,7 +325,7 @@
         synchronized(m_loggerRefs)
         {
             final String logMsg = "EventAdmin: " + msg;
-            
+
             if (!m_loggerRefs.isEmpty())
             {
                 // There is at least one LogService available hence, we can use the
@@ -298,19 +333,19 @@
                 for (Iterator iter = m_loggerRefs.iterator(); iter.hasNext();)
                 {
                     final ServiceReference next = (ServiceReference) iter.next();
-                       
-                    org.osgi.service.log.LogService logger = 
+
+                    org.osgi.service.log.LogService logger =
                         (org.osgi.service.log.LogService) m_context.getService(next);
-    
+
                     if (null != logger)
                     {
                         logger.log(sr, level, logMsg);
-                        
+
                         m_context.ungetService(next);
                     }
                     else
                     {
-                        // The context returned null for the reference - it follows 
+                        // The context returned null for the reference - it follows
                         // that the service is unregistered and we can remove it
                         iter.remove();
                     }
@@ -326,20 +361,20 @@
     /**
      * Log a message with the given log level, the associated service reference and
      * exception. Note that this will prefix the message with <tt>EventAdmin: </tt>.
-     * 
+     *
      * @param sr The reference of the service associated with this message.
      * @param level The log level with which to log the msg.
      * @param msg The message to log.
      * @param ex The exception associated with the message.
      */
-    public void log(final ServiceReference sr, final int level, final String msg, 
+    public void log(final ServiceReference sr, final int level, final String msg,
         final Throwable ex)
     {
         // The method will remove any unregistered service reference as well.
         synchronized(m_loggerRefs)
         {
             final String logMsg = "EventAdmin: " + msg;
-            
+
             if (!m_loggerRefs.isEmpty())
             {
                 // There is at least one LogService available hence, we can use the
@@ -347,19 +382,19 @@
                 for (Iterator iter = m_loggerRefs.iterator(); iter.hasNext();)
                 {
                        final ServiceReference next = (ServiceReference) iter.next();
-                       
-                    org.osgi.service.log.LogService logger = 
+
+                    org.osgi.service.log.LogService logger =
                         (org.osgi.service.log.LogService) m_context.getService(next);
-    
+
                     if (null != logger)
                     {
                         logger.log(sr, level, logMsg, ex);
-                        
+
                         m_context.ungetService(next);
                     }
                     else
                     {
-                        // The context returned null for the reference - it follows 
+                        // The context returned null for the reference - it follows
                         // that the service is unregistered and we can remove it
                         iter.remove();
                     }
@@ -374,15 +409,15 @@
 
     /*
      * Log the message to standard output. This appends the level to the message.
-     * null values are handled appropriate. 
+     * null values are handled appropriate.
      */
-    private void _log(final ServiceReference sr, final int level, final String msg, 
+    private void _log(final ServiceReference sr, final int level, final String msg,
         Throwable ex)
     {
         String s = (sr == null) ? null : "SvcRef " + sr;
         s = (s == null) ? msg : s + " " + msg;
         s = (ex == null) ? s : s + " (" + ex + ")";
-        
+
         switch (level)
         {
             case LOG_DEBUG:
@@ -397,7 +432,7 @@
                     {
                         ex = ((BundleException) ex).getNestedException();
                     }
-                    
+
                     ex.printStackTrace();
                 }
                 break;