FELIX-4466 - DA does not always fire events

- make sure that an event is fired, even when a deployment operation
  fails;
- added integration tests to verify this behaviour.



git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1581373 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/deploymentadmin/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/DeploymentAdminImpl.java b/deploymentadmin/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/DeploymentAdminImpl.java
index 320f51d..aeb2624 100644
--- a/deploymentadmin/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/DeploymentAdminImpl.java
+++ b/deploymentadmin/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/DeploymentAdminImpl.java
@@ -277,9 +277,7 @@
                 	succeeded = false;
                 }
             }
-        	if (source != null) {
-        	    sendCompleteEvent(source, target, succeeded);
-        	}
+    	    sendCompleteEvent(source, target, succeeded);
             m_semaphore.release();
         }
     }
@@ -366,9 +364,7 @@
             succeeded = true;
         }
         finally {
-            if (source != null) {
-                sendCompleteEvent(source, target, succeeded);
-            }
+            sendCompleteEvent(source, target, succeeded);
             m_semaphore.release();
         }
     }
diff --git a/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/DeploymentAdminEventTest.java b/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/DeploymentAdminEventTest.java
new file mode 100644
index 0000000..72c60d6
--- /dev/null
+++ b/deploymentadmin/itest/src/test/java/org/apache/felix/deploymentadmin/itest/DeploymentAdminEventTest.java
@@ -0,0 +1,303 @@
+/*
+ * 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.deploymentadmin.itest;
+
+import java.util.Dictionary;
+import java.util.Properties;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.felix.deploymentadmin.Constants;
+import org.apache.felix.deploymentadmin.itest.util.DeploymentPackageBuilder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.deploymentadmin.DeploymentException;
+import org.osgi.service.deploymentadmin.DeploymentPackage;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventConstants;
+import org.osgi.service.event.EventHandler;
+
+/**
+ * Test cases for FELIX-4466 - DA does not always fire events.
+ */
+@RunWith(PaxExam.class)
+public class DeploymentAdminEventTest extends BaseIntegrationTest
+{
+    /**
+     * FELIX-4466 - test that an event is fired when an installation of a DP fails.
+     */
+    @Test
+    public void testFailedInstallationCausesCompletionEventOk() throws Exception
+    {
+        DeploymentPackageBuilder dpBuilder = createNewDeploymentPackageBuilder("1.0.0");
+        // incluse two different versions of the same bundle (with the same BSN), this is *not* allowed per the DA spec...
+        dpBuilder
+            .add(dpBuilder.createBundleResource().setUrl(getTestBundle("bundleapi1", "bundleapi", "1.0.0")))
+            .add(dpBuilder.createBundleResource().setUrl(getTestBundle("bundleapi2", "bundleapi", "2.0.0")));
+
+        final AtomicReference<Event> completionEventRef = new AtomicReference<Event>();
+        final AtomicReference<Event> installEventRef = new AtomicReference<Event>();
+        final CountDownLatch cdl = new CountDownLatch(1);
+
+        EventHandler eventHandler = new EventHandler()
+        {
+            @Override
+            public void handleEvent(Event event)
+            {
+                if (Constants.EVENTTOPIC_COMPLETE.equals(event.getTopic()))
+                {
+                    assertTrue("Multiple events received?!", completionEventRef.compareAndSet(null, event));
+                    cdl.countDown();
+                }
+                else if (Constants.EVENTTOPIC_INSTALL.equals(event.getTopic()))
+                {
+                    assertTrue("Multiple events received?!", installEventRef.compareAndSet(null, event));
+                }
+            }
+        };
+
+        Dictionary props = new Properties();
+        props.put(EventConstants.EVENT_TOPIC, new String[] { Constants.EVENTTOPIC_COMPLETE, Constants.EVENTTOPIC_INSTALL });
+
+        ServiceRegistration sreg = m_context.registerService(EventHandler.class, eventHandler, props);
+
+        try
+        {
+            m_deploymentAdmin.installDeploymentPackage(dpBuilder.generate());
+            fail("DeploymentException expected!");
+        }
+        catch (DeploymentException e)
+        {
+            // Ok; expected...
+            assertTrue("Not all events were received in time?!", cdl.await(5, TimeUnit.SECONDS));
+
+            Event event;
+            // Verify we've got the expected events...
+            event = installEventRef.get();
+            // The install event is send *after* the DP have been created, which fails in this test...
+            assertNull("No install event received?!", event);
+            
+            event = completionEventRef.get();
+            assertNotNull("No completion event received?!", event);
+            assertTrue("Completion property set to true?!", Boolean.FALSE.equals(event.getProperty(Constants.EVENTPROPERTY_SUCCESSFUL)));
+        }
+        finally
+        {
+            sreg.unregister();
+        }
+    }
+
+    /**
+     * FELIX-4466 - test that an event is fired when an installation of a DP succeeds.
+     */
+    @Test
+    public void testSuccessfulInstallationCausesCompletionEventOk() throws Exception
+    {
+        DeploymentPackageBuilder dpBuilder = createNewDeploymentPackageBuilder("1.0.0");
+        dpBuilder
+            .add(dpBuilder.createBundleResource().setUrl(getTestBundle("bundleapi1", "bundleapi", "1.0.0")))
+            .add(dpBuilder.createBundleResource().setUrl(getTestBundle("bundleimpl1", "bundleimpl", "1.0.0")));
+
+        final AtomicReference<Event> completionEventRef = new AtomicReference<Event>();
+        final AtomicReference<Event> installEventRef = new AtomicReference<Event>();
+        final CountDownLatch cdl = new CountDownLatch(2);
+        
+        EventHandler eventHandler = new EventHandler()
+        {
+            @Override
+            public void handleEvent(Event event)
+            {
+                if (Constants.EVENTTOPIC_COMPLETE.equals(event.getTopic()))
+                {
+                    assertTrue("Multiple events received?!", completionEventRef.compareAndSet(null, event));
+                    cdl.countDown();
+                }
+                else if (Constants.EVENTTOPIC_INSTALL.equals(event.getTopic()))
+                {
+                    assertTrue("Multiple events received?!", installEventRef.compareAndSet(null, event));
+                    cdl.countDown();
+                }
+            }
+        };
+
+        Dictionary props = new Properties();
+        props.put(EventConstants.EVENT_TOPIC, new String[] { Constants.EVENTTOPIC_COMPLETE, Constants.EVENTTOPIC_INSTALL });
+
+        ServiceRegistration sreg = m_context.registerService(EventHandler.class, eventHandler, props);
+
+        try
+        {
+            m_deploymentAdmin.installDeploymentPackage(dpBuilder.generate());
+
+            assertTrue("Not all events were received in time?!", cdl.await(5, TimeUnit.SECONDS));
+            
+            Event event;
+            // Verify we've got the expected events...
+            event = installEventRef.get();
+            assertNotNull("No install event received?!", event);
+            
+            event = completionEventRef.get();
+            assertNotNull("No completion event received?!", event);
+            assertTrue("Completion property set to false?!", Boolean.TRUE.equals(event.getProperty(Constants.EVENTPROPERTY_SUCCESSFUL)));
+        }
+        finally
+        {
+            sreg.unregister();
+        }
+    }
+
+    /**
+     * FELIX-4466 - test that an event is fired when a DP is uninstalled.
+     */
+    @Test
+    public void testSuccessfulUninstallationCausesCompletionEventOk() throws Exception
+    {
+        DeploymentPackageBuilder dpBuilder = createNewDeploymentPackageBuilder("1.0.0");
+        dpBuilder
+            .add(dpBuilder.createBundleResource().setUrl(getTestBundle("bundleapi1", "bundleapi", "1.0.0")))
+            .add(dpBuilder.createBundleResource().setUrl(getTestBundle("bundleimpl1", "bundleimpl", "1.0.0")));
+
+        final AtomicReference<Event> completionEventRef = new AtomicReference<Event>();
+        final AtomicReference<Event> uninstallEventRef = new AtomicReference<Event>();
+        final CountDownLatch cdl = new CountDownLatch(2);
+        
+        EventHandler eventHandler = new EventHandler()
+        {
+            @Override
+            public void handleEvent(Event event)
+            {
+                if (Constants.EVENTTOPIC_COMPLETE.equals(event.getTopic()))
+                {
+                    assertTrue("Multiple events received?!", completionEventRef.compareAndSet(null, event));
+                    cdl.countDown();
+                }
+                else if (Constants.EVENTTOPIC_UNINSTALL.equals(event.getTopic()))
+                {
+                    assertTrue("Multiple events received?!", uninstallEventRef.compareAndSet(null, event));
+                    cdl.countDown();
+                }
+            }
+        };
+
+        DeploymentPackage dp = m_deploymentAdmin.installDeploymentPackage(dpBuilder.generate());
+        assertNotNull(dp);
+        
+        awaitRefreshPackagesEvent();
+
+        Dictionary props = new Properties();
+        props.put(EventConstants.EVENT_TOPIC, new String[] { Constants.EVENTTOPIC_COMPLETE, Constants.EVENTTOPIC_UNINSTALL });
+
+        ServiceRegistration sreg = m_context.registerService(EventHandler.class, eventHandler, props);
+
+        try
+        {
+            dp.uninstall();
+
+            assertTrue("Not all events were received in time?!", cdl.await(5, TimeUnit.SECONDS));
+
+            Event event;
+            // Verify we've got the expected events...
+            event = uninstallEventRef.get();
+            assertNotNull("No uninstall event received?!", event);
+            
+            event = completionEventRef.get();
+            assertNotNull("No completion event received?!", event);
+            assertTrue("Completion property set to false?!", Boolean.TRUE.equals(event.getProperty(Constants.EVENTPROPERTY_SUCCESSFUL)));
+        }
+        finally
+        {
+            sreg.unregister();
+        }
+    }
+
+    /**
+     * FELIX-4466 - test that an event is fired when a DP is uninstalled, but fails.
+     */
+    @Test
+    public void testFailedUninstallationCausesCompletionEventOk() throws Exception
+    {
+        DeploymentPackageBuilder dpBuilder = createNewDeploymentPackageBuilder("1.0.0");
+        dpBuilder
+            .add(dpBuilder.createBundleResource().setUrl(getTestBundle("bundle1")))
+            .add(dpBuilder.createResourceProcessorResource().setUrl(getTestBundle("rp1")))
+            .add(dpBuilder.createResource().setResourceProcessorPID(TEST_FAILING_BUNDLE_RP1).setUrl(getTestResource("test-config1.xml")));
+
+        final AtomicReference<Event> completionEventRef = new AtomicReference<Event>();
+        final AtomicReference<Event> uninstallEventRef = new AtomicReference<Event>();
+        final CountDownLatch cdl = new CountDownLatch(2);
+
+        EventHandler eventHandler = new EventHandler()
+        {
+            @Override
+            public void handleEvent(Event event)
+            {
+                if (Constants.EVENTTOPIC_COMPLETE.equals(event.getTopic()))
+                {
+                    assertTrue("Multiple events received?!", completionEventRef.compareAndSet(null, event));
+                    cdl.countDown();
+                }
+                else if (Constants.EVENTTOPIC_UNINSTALL.equals(event.getTopic()))
+                {
+                    assertTrue("Multiple events received?!", uninstallEventRef.compareAndSet(null, event));
+                    cdl.countDown();
+                }
+            }
+        };
+
+        DeploymentPackage dp = m_deploymentAdmin.installDeploymentPackage(dpBuilder.generate());
+        assertNotNull("No deployment package returned?!", dp);
+
+        awaitRefreshPackagesEvent();
+
+        // Should cause the uninstall of the DP to fail...
+        dp.getBundle(getSymbolicName("rp1")).uninstall();
+
+        Dictionary props = new Properties();
+        props.put(EventConstants.EVENT_TOPIC, new String[] { Constants.EVENTTOPIC_COMPLETE, Constants.EVENTTOPIC_UNINSTALL });
+
+        ServiceRegistration sreg = m_context.registerService(EventHandler.class, eventHandler, props);
+
+        try
+        {
+            dp.uninstall();
+            fail("DeploymentException expected!");
+        }
+        catch (DeploymentException e)
+        {
+            // Ok, expected...
+            assertTrue("Not all events were received in time?!", cdl.await(5, TimeUnit.SECONDS));
+
+            Event event;
+            // Verify we've got the expected events...
+            event = uninstallEventRef.get();
+            assertNotNull("No uninstall event received?!", event);
+
+            event = completionEventRef.get();
+            assertNotNull("No completion event received?!", event);
+            assertTrue("Completion property set to true?!", Boolean.FALSE.equals(event.getProperty(Constants.EVENTPROPERTY_SUCCESSFUL)));
+        }
+        finally
+        {
+            sreg.unregister();
+        }
+    }
+}