FELIX-3596 ConfigurationEvents should only be synchronously delivered to listeners registered as SynchronousConfigurationEventListener (this is a service interface not a marker interface). Adapted test cases to verify async delivery to SynchronousConfigurationEventListener implementations registered as ConfigurationEventListener services.

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1361527 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationManager.java b/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationManager.java
index 9eccab3..b0d1ba4 100644
--- a/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationManager.java
+++ b/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationManager.java
@@ -135,6 +135,9 @@
     // the ConfigurationEvent listeners
     private ServiceTracker configurationListenerTracker;
 
+    // the synchronous ConfigurationEvent listeners
+    private ServiceTracker syncConfigurationListenerTracker;
+
     // service tracker for managed services
     private ManagedServiceTracker managedServiceTracker;
 
@@ -221,6 +224,9 @@
         // configurationlistener support
         configurationListenerTracker = new ServiceTracker( bundleContext, ConfigurationListener.class.getName(), null );
         configurationListenerTracker.open();
+        syncConfigurationListenerTracker = new ServiceTracker( bundleContext,
+            SynchronousConfigurationListener.class.getName(), null );
+        syncConfigurationListenerTracker.open();
 
         // initialize the asynchonous updater thread
         ThreadGroup tg = new ThreadGroup( "Configuration Admin Service" );
@@ -330,6 +336,11 @@
             configurationListenerTracker.close();
         }
 
+        if ( syncConfigurationListenerTracker != null )
+        {
+            syncConfigurationListenerTracker.close();
+        }
+
         if ( logTracker != null )
         {
             logTracker.close();
@@ -734,16 +745,32 @@
 
     void fireConfigurationEvent( int type, String pid, String factoryPid )
     {
-        FireConfigurationEvent event = new FireConfigurationEvent( type, pid, factoryPid );
-        event.fireSynchronousEvents();
-        if ( event.hasConfigurationEventListeners() )
+        // prevent event senders
+        FireConfigurationEvent asyncSender = new FireConfigurationEvent( this.configurationListenerTracker, type, pid,
+            factoryPid );
+        FireConfigurationEvent syncSender = new FireConfigurationEvent( this.syncConfigurationListenerTracker, type,
+            pid, factoryPid );
+
+        // send synchronous events
+        if ( syncSender.hasConfigurationEventListeners() )
         {
-            eventThread.schedule( event );
+            syncSender.run();
+        }
+        else
+        {
+            log( LogService.LOG_DEBUG, "No SynchronousConfigurationListeners to send {0} event to.", new Object[]
+                { syncSender.getTypeName() } );
+        }
+
+        // schedule asynchronous events
+        if ( asyncSender.hasConfigurationEventListeners() )
+        {
+            eventThread.schedule( asyncSender );
         }
         else
         {
             log( LogService.LOG_DEBUG, "No ConfigurationListeners to send {0} event to.", new Object[]
-                { event.getTypeName() } );
+                { asyncSender.getTypeName() } );
         }
     }
 
@@ -1890,13 +1917,13 @@
 
         private ConfigurationEvent event;
 
-        private FireConfigurationEvent( final int type, final String pid, final String factoryPid)
+        private FireConfigurationEvent( final ServiceTracker listenerTracker, final int type, final String pid, final String factoryPid)
         {
             this.type = type;
             this.pid = pid;
             this.factoryPid = factoryPid;
 
-            final ServiceReference[] srs = configurationListenerTracker.getServiceReferences();
+            final ServiceReference[] srs = listenerTracker.getServiceReferences();
             if ( srs == null || srs.length == 0 )
             {
                 this.listenerReferences = null;
@@ -1910,28 +1937,13 @@
                 this.listenerProvider = new Bundle[srs.length];
                 for ( int i = 0; i < srs.length; i++ )
                 {
-                    this.listeners[i] = ( ConfigurationListener ) configurationListenerTracker.getService( srs[i] );
+                    this.listeners[i] = ( ConfigurationListener ) listenerTracker.getService( srs[i] );
                     this.listenerProvider[i] = srs[i].getBundle();
                 }
             }
         }
 
 
-        void fireSynchronousEvents()
-        {
-            if ( hasConfigurationEventListeners() && getServiceReference() != null )
-            {
-                for ( int i = 0; i < this.listeners.length; i++ )
-                {
-                    if ( this.listeners[i] instanceof SynchronousConfigurationListener )
-                    {
-                        sendEvent( i );
-                    }
-                }
-            }
-        }
-
-
         boolean hasConfigurationEventListeners()
         {
             return this.listenerReferences != null;
diff --git a/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationListenerTest.java b/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationListenerTest.java
index 3301a19..4961d00 100644
--- a/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationListenerTest.java
+++ b/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationListenerTest.java
@@ -31,6 +31,7 @@
 import org.osgi.service.cm.Configuration;
 import org.osgi.service.cm.ConfigurationEvent;
 import org.osgi.service.cm.ConfigurationListener;
+import org.osgi.service.cm.SynchronousConfigurationListener;
 
 
 @RunWith(JUnit4TestRunner.class)
@@ -113,14 +114,27 @@
     {
         final String pid = "test_listener";
         Configuration config = configure( pid, null, false );
+
+        // Synchronous listener expecting synchronous events being
+        // registered as a SynchronousConfigurationListener
         final TestListener testListener = new SynchronousTestListener();
-        final ServiceRegistration listener = this.bundleContext.registerService( ConfigurationListener.class.getName(),
-            testListener, null );
+        final ServiceRegistration listener = this.bundleContext.registerService(
+            SynchronousConfigurationListener.class.getName(), testListener, null );
+
+        // Synchronous listener expecting asynchronous events being
+        // registered as a regular ConfigurationListener
+        final TestListener testListenerAsync = new SynchronousTestListener();
+        final ServiceRegistration listenerAsync = this.bundleContext.registerService(
+            ConfigurationListener.class.getName(), testListenerAsync, null );
+
         int eventCount = 0;
+        int eventCountAsync = 0;
+
         try
         {
             delay();
             testListener.assertNoEvent();
+            testListenerAsync.assertNoEvent();
 
             config.update( new Hashtable<String, Object>()
             {
@@ -130,6 +144,7 @@
             } );
             delay();
             testListener.assertEvent( ConfigurationEvent.CM_UPDATED, pid, null, false, ++eventCount );
+            testListenerAsync.assertEvent( ConfigurationEvent.CM_UPDATED, pid, null, true, ++eventCountAsync );
 
             config.update( new Hashtable<String, Object>()
             {
@@ -139,18 +154,22 @@
             } );
             delay();
             testListener.assertEvent( ConfigurationEvent.CM_UPDATED, pid, null, false, ++eventCount );
+            testListenerAsync.assertEvent( ConfigurationEvent.CM_UPDATED, pid, null, true, ++eventCountAsync );
 
             config.setBundleLocation( "new_Location" );
             delay();
             testListener.assertEvent( ConfigurationEvent.CM_LOCATION_CHANGED, pid, null, false, ++eventCount );
+            testListenerAsync.assertEvent( ConfigurationEvent.CM_LOCATION_CHANGED, pid, null, true, ++eventCountAsync );
 
             config.update();
             testListener.assertNoEvent();
+            testListenerAsync.assertNoEvent();
 
             config.delete();
             config = null;
             delay();
             testListener.assertEvent( ConfigurationEvent.CM_DELETED, pid, null, false, ++eventCount );
+            testListenerAsync.assertEvent( ConfigurationEvent.CM_DELETED, pid, null, true, ++eventCountAsync );
         }
         finally
         {
@@ -167,6 +186,7 @@
             }
 
             listener.unregister();
+            listenerAsync.unregister();
         }
     }
 }
diff --git a/configadmin/src/test/java/org/apache/felix/cm/integration/FELIX2813_ConfigurationAdminStartupTest.java b/configadmin/src/test/java/org/apache/felix/cm/integration/FELIX2813_ConfigurationAdminStartupTest.java
index 988f192..c69e47c 100644
--- a/configadmin/src/test/java/org/apache/felix/cm/integration/FELIX2813_ConfigurationAdminStartupTest.java
+++ b/configadmin/src/test/java/org/apache/felix/cm/integration/FELIX2813_ConfigurationAdminStartupTest.java
@@ -38,6 +38,7 @@
 import org.osgi.service.cm.ConfigurationAdmin;
 import org.osgi.service.cm.ConfigurationEvent;
 import org.osgi.service.cm.ConfigurationListener;
+import org.osgi.service.cm.SynchronousConfigurationListener;
 
 
 @RunWith(JUnit4TestRunner.class)
@@ -62,7 +63,9 @@
         final TestListener listener = new TestListener();
         bundleContext.registerService( ConfigurationListener.class.getName(), listener, null );
         final TestListener syncListener = new SynchronousTestListener();
-        bundleContext.registerService( ConfigurationListener.class.getName(), syncListener, null );
+        bundleContext.registerService( SynchronousConfigurationListener.class.getName(), syncListener, null );
+        final TestListener syncListenerAsync = new SynchronousTestListener();
+        bundleContext.registerService( ConfigurationListener.class.getName(), syncListenerAsync, null );
         bundleContext.addServiceListener( this, "(" + Constants.OBJECTCLASS + "=" + ConfigurationAdmin.class.getName()
             + ")" );
 
@@ -95,6 +98,7 @@
         delay();
         listener.assertEvent( ConfigurationEvent.CM_UPDATED, "test", null, true, 1 );
         syncListener.assertEvent( ConfigurationEvent.CM_UPDATED, "test", null, false, 1 );
+        syncListenerAsync.assertEvent( ConfigurationEvent.CM_UPDATED, "test", null, true, 1 );
     }