Author: fmeschbe
Date: Wed Apr 25 08:50:30 2012
New Revision: 1330159

URL: http://svn.apache.org/viewvc?rev=1330159&view=rev
Log:
FELIX-3480 Implement support for SynchronousConfigurationListener

Added:
    
felix/sandbox/fmeschbe/configadmin-R5/src/test/java/org/apache/felix/cm/integration/ConfigurationListenerTest.java
Modified:
    
felix/sandbox/fmeschbe/configadmin-R5/src/main/java/org/apache/felix/cm/impl/ConfigurationManager.java

Modified: 
felix/sandbox/fmeschbe/configadmin-R5/src/main/java/org/apache/felix/cm/impl/ConfigurationManager.java
URL: 
http://svn.apache.org/viewvc/felix/sandbox/fmeschbe/configadmin-R5/src/main/java/org/apache/felix/cm/impl/ConfigurationManager.java?rev=1330159&r1=1330158&r2=1330159&view=diff
==============================================================================
--- 
felix/sandbox/fmeschbe/configadmin-R5/src/main/java/org/apache/felix/cm/impl/ConfigurationManager.java
 (original)
+++ 
felix/sandbox/fmeschbe/configadmin-R5/src/main/java/org/apache/felix/cm/impl/ConfigurationManager.java
 Wed Apr 25 08:50:30 2012
@@ -635,6 +635,7 @@ public class ConfigurationManager implem
     void fireConfigurationEvent( int type, String pid, String factoryPid )
     {
         FireConfigurationEvent event = new FireConfigurationEvent( type, pid, 
factoryPid );
+        event.fireSynchronousEvents();
         if ( event.hasConfigurationEventListeners() )
         {
             eventThread.schedule( event );
@@ -1937,6 +1938,23 @@ public class ConfigurationManager implem
         }
 
 
+        void fireSynchronousEvents()
+        {
+            if ( hasConfigurationEventListeners() )
+            {
+                final String typeName = getTypeName();
+                final ConfigurationEvent event = createEvent();
+                for ( int i = 0; i < this.listeners.length; i++ )
+                {
+                    if ( this.listeners[i] instanceof 
SynchronousConfigurationListener )
+                    {
+                        sendEvent( typeName, i, event );
+                    }
+                }
+            }
+        }
+
+
         boolean hasConfigurationEventListeners()
         {
             return this.listenerReferences != null;
@@ -1962,26 +1980,11 @@ public class ConfigurationManager implem
         public void run()
         {
             final String typeName = getTypeName();
-            final ConfigurationEvent event = new ConfigurationEvent( 
getServiceReference(), type, factoryPid, pid );
+            final ConfigurationEvent event = createEvent();
 
             for ( int i = 0; i < listeners.length; i++ )
             {
-                if ( listenerProvider[i].getState() == Bundle.ACTIVE )
-                {
-                    log( LogService.LOG_DEBUG, "Sending {0} event for {1} to 
{2}", new Object[]
-                        { typeName, pid, ConfigurationManager.toString( 
listenerReferences[i] ) } );
-
-                    try
-                    {
-                        listeners[i].configurationEvent( event );
-                    }
-                    catch ( Throwable t )
-                    {
-                        log( LogService.LOG_ERROR, "Unexpected problem 
delivering configuration event to {0}",
-                            new Object[]
-                                { ConfigurationManager.toString( 
listenerReferences[i] ), t } );
-                    }
-                }
+                sendEvent( typeName, i, event );
             }
         }
 
@@ -1989,6 +1992,36 @@ public class ConfigurationManager implem
         {
             return "Fire ConfigurationEvent: pid=" + pid;
         }
+
+
+        private ConfigurationEvent createEvent()
+        {
+            return new ConfigurationEvent( getServiceReference(), type, 
factoryPid, pid );
+        }
+
+
+        private void sendEvent( final String typeName, final int serviceIndex, 
final ConfigurationEvent event )
+        {
+            if ( listenerProvider[serviceIndex].getState() == Bundle.ACTIVE && 
this.listeners[serviceIndex] != null )
+            {
+                log( LogService.LOG_DEBUG, "Sending {0} event for {1} to {2}", 
new Object[]
+                    { typeName, pid, ConfigurationManager.toString( 
listenerReferences[serviceIndex] ) } );
+
+                try
+                {
+                    listeners[serviceIndex].configurationEvent( event );
+                }
+                catch ( Throwable t )
+                {
+                    log( LogService.LOG_ERROR, "Unexpected problem delivering 
configuration event to {0}", new Object[]
+                        { ConfigurationManager.toString( 
listenerReferences[serviceIndex] ), t } );
+                }
+                finally
+                {
+                    this.listeners[serviceIndex] = null;
+                }
+            }
+        }
     }
 
     private static class ManagedServiceTracker extends ServiceTracker

Added: 
felix/sandbox/fmeschbe/configadmin-R5/src/test/java/org/apache/felix/cm/integration/ConfigurationListenerTest.java
URL: 
http://svn.apache.org/viewvc/felix/sandbox/fmeschbe/configadmin-R5/src/test/java/org/apache/felix/cm/integration/ConfigurationListenerTest.java?rev=1330159&view=auto
==============================================================================
--- 
felix/sandbox/fmeschbe/configadmin-R5/src/test/java/org/apache/felix/cm/integration/ConfigurationListenerTest.java
 (added)
+++ 
felix/sandbox/fmeschbe/configadmin-R5/src/test/java/org/apache/felix/cm/integration/ConfigurationListenerTest.java
 Wed Apr 25 08:50:30 2012
@@ -0,0 +1,261 @@
+/*
+ * 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.cm.integration;
+
+
+import java.io.IOException;
+import java.util.Hashtable;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.JUnit4TestRunner;
+import org.osgi.framework.ServiceRegistration;
+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)
+public class ConfigurationListenerTest extends ConfigurationTestBase
+{
+
+    static
+    {
+        // uncomment to enable debugging of this test class
+        // paxRunnerVmOption = DEBUG_VM_OPTION;
+    }
+
+
+    @Test
+    public void test_async_listener() throws IOException
+    {
+        final String pid = "test_listener";
+        final TestListener testListener = new TestListener();
+        final ServiceRegistration listener = 
this.bundleContext.registerService( ConfigurationListener.class.getName(),
+            testListener, null );
+        int eventCount = 0;
+
+        Configuration config = configure( pid, null, false );
+        try
+        {
+            delay();
+            testListener.assertNoEvent();
+
+            config.update( new Hashtable<String, Object>()
+            {
+                {
+                    put( "x", "x" );
+                }
+            } );
+            delay();
+            testListener.assertEvent( ConfigurationEvent.CM_UPDATED, pid, 
null, true, ++eventCount );
+
+            config.update( new Hashtable<String, Object>()
+            {
+                {
+                    put( "x", "x" );
+                }
+            } );
+            delay();
+            testListener.assertEvent( ConfigurationEvent.CM_UPDATED, pid, 
null, true, ++eventCount );
+
+            config.setBundleLocation( "new_Location" );
+            delay();
+            testListener.assertEvent( ConfigurationEvent.CM_LOCATION_CHANGED, 
pid, null, true, ++eventCount );
+
+            config.update();
+            testListener.assertNoEvent();
+
+            config.delete();
+            config = null;
+            delay();
+            testListener.assertEvent( ConfigurationEvent.CM_DELETED, pid, 
null, true, ++eventCount );
+        }
+        finally
+        {
+            if ( config != null )
+            {
+                try
+                {
+                    config.delete();
+                }
+                catch ( IOException ioe )
+                {
+                    // ignore
+                }
+            }
+
+            listener.unregister();
+        }
+    }
+
+
+    @Test
+    public void test_sync_listener() throws IOException
+    {
+        final String pid = "test_listener";
+        Configuration config = configure( pid, null, false );
+        final TestListener testListener = new SynchronousTestListener();
+        final ServiceRegistration listener = 
this.bundleContext.registerService( ConfigurationListener.class.getName(),
+            testListener, null );
+        int eventCount = 0;
+        try
+        {
+            delay();
+            testListener.assertNoEvent();
+
+            config.update( new Hashtable<String, Object>()
+            {
+                {
+                    put( "x", "x" );
+                }
+            } );
+            delay();
+            testListener.assertEvent( ConfigurationEvent.CM_UPDATED, pid, 
null, false, ++eventCount );
+
+            config.update( new Hashtable<String, Object>()
+            {
+                {
+                    put( "x", "x" );
+                }
+            } );
+            delay();
+            testListener.assertEvent( ConfigurationEvent.CM_UPDATED, pid, 
null, false, ++eventCount );
+
+            config.setBundleLocation( "new_Location" );
+            delay();
+            testListener.assertEvent( ConfigurationEvent.CM_LOCATION_CHANGED, 
pid, null, false, ++eventCount );
+
+            config.update();
+            testListener.assertNoEvent();
+
+            config.delete();
+            config = null;
+            delay();
+            testListener.assertEvent( ConfigurationEvent.CM_DELETED, pid, 
null, false, ++eventCount );
+        }
+        finally
+        {
+            if ( config != null )
+            {
+                try
+                {
+                    config.delete();
+                }
+                catch ( IOException ioe )
+                {
+                    // ignore
+                }
+            }
+
+            listener.unregister();
+        }
+    }
+
+    private static class TestListener implements ConfigurationListener
+    {
+
+        private final Thread mainThread;
+
+        private ConfigurationEvent event;
+
+        private Thread eventThread;
+
+        private int numberOfEvents;
+
+
+        TestListener()
+        {
+            this.mainThread = Thread.currentThread();
+            this.numberOfEvents = 0;
+        }
+
+
+        public void configurationEvent( final ConfigurationEvent event )
+        {
+            this.numberOfEvents++;
+
+            if ( this.event != null )
+            {
+                throw new IllegalStateException( "Untested event to be 
replaced: " + this.event.getType() + "/"
+                    + this.event.getPid() );
+            }
+
+            this.event = event;
+            this.eventThread = Thread.currentThread();
+        }
+
+
+        void resetNumberOfEvents()
+        {
+            this.numberOfEvents = 0;
+        }
+
+
+        void assertEvent( final int type, final String pid, final String 
factoryPid, final boolean expectAsync,
+            final int numberOfEvents )
+        {
+            try
+            {
+                TestCase.assertNotNull( "Expecting an event", this.event );
+                TestCase.assertEquals( "Expecting event type " + type, type, 
this.event.getType() );
+                TestCase.assertEquals( "Expecting pid " + pid, pid, 
this.event.getPid() );
+                if ( factoryPid == null )
+                {
+                    TestCase.assertNull( "Expecting no factoryPid", 
this.event.getFactoryPid() );
+                }
+                else
+                {
+                    TestCase.assertEquals( "Expecting factory pid " + 
factoryPid, factoryPid,
+                        this.event.getFactoryPid() );
+                }
+
+                TestCase.assertEquals( "Expecting " + numberOfEvents + " 
events", numberOfEvents,
+                    this.numberOfEvents );
+
+                if ( expectAsync )
+                {
+                    TestCase.assertNotSame( "Expecting asynchronous event", 
this.mainThread, this.eventThread );
+                }
+                else
+                {
+                    TestCase.assertSame( "Expecting synchronous event", 
this.mainThread, this.eventThread );
+                }
+            }
+            finally
+            {
+                this.event = null;
+                this.eventThread = null;
+            }
+        }
+
+
+        void assertNoEvent()
+        {
+            TestCase.assertNull( this.event );
+        }
+    }
+
+    private static class SynchronousTestListener extends TestListener 
implements SynchronousConfigurationListener
+    {
+    }
+}


Reply via email to