SLING-1031 : Send events when a new adapter factory is added or an old one is removed.

git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@790513 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/pom.xml b/pom.xml
index 12e3fbd..83da7fb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -84,7 +84,7 @@
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.api</artifactId>
-            <version>2.0.2-incubator</version>
+            <version>2.0.5-SNAPSHOT</version>
         </dependency>
         <dependency>
             <groupId>org.apache.sling</groupId>
diff --git a/src/main/java/org/apache/sling/adapter/internal/AdapterManagerImpl.java b/src/main/java/org/apache/sling/adapter/internal/AdapterManagerImpl.java
index 4be2807..6c2d26f 100644
--- a/src/main/java/org/apache/sling/adapter/internal/AdapterManagerImpl.java
+++ b/src/main/java/org/apache/sling/adapter/internal/AdapterManagerImpl.java
@@ -21,17 +21,23 @@
 import static org.apache.sling.api.adapter.AdapterFactory.ADAPTABLE_CLASSES;
 import static org.apache.sling.api.adapter.AdapterFactory.ADAPTER_CLASSES;
 
+import java.util.Dictionary;
 import java.util.HashMap;
+import java.util.Hashtable;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.sling.api.SlingConstants;
 import org.apache.sling.api.adapter.AdapterFactory;
 import org.apache.sling.api.adapter.AdapterManager;
 import org.apache.sling.commons.osgi.OsgiUtil;
 import org.osgi.framework.ServiceReference;
 import org.osgi.service.component.ComponentContext;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
 import org.osgi.service.log.LogService;
+import org.osgi.util.tracker.ServiceTracker;
 
 /**
  * The <code>AdapterManagerImpl</code> class implements the
@@ -103,6 +109,10 @@
      */
     private Map<String, Map<String, AdapterFactory>> factoryCache;
 
+    /** The service tracker for the event admin
+     */
+    private ServiceTracker eventAdminTracker;
+
     // ---------- AdapterManager interface -------------------------------------
 
     /**
@@ -140,6 +150,10 @@
     // ----------- SCR integration ---------------------------------------------
 
     protected synchronized void activate(ComponentContext context) {
+        // setup tracker first as this is used in the bind/unbind methods
+        this.eventAdminTracker = new ServiceTracker(context.getBundleContext(),
+                EventAdmin.class.getName(), null);
+        this.eventAdminTracker.open();
         this.context = context;
 
         // register all adapter factories bound before activation
@@ -173,7 +187,10 @@
                 "Not clearing instance field: Set to another manager "
                     + AdapterManagerImpl.INSTANCE, null);
         }
-
+        if ( this.eventAdminTracker != null ) {
+            this.eventAdminTracker.close();
+            this.eventAdminTracker = null;
+        }
         this.context = null;
     }
 
@@ -226,13 +243,21 @@
     }
 
     /**
+     * Get the event admin.
+     * @return The event admin or <code>null</code>
+     */
+    private EventAdmin getEventAdmin() {
+        return (EventAdmin) (this.eventAdminTracker != null ? this.eventAdminTracker.getService() : null);
+    }
+
+    /**
      * Unregisters the {@link AdapterFactory} referred to by the service
      * <code>reference</code> from the registry.
      */
     private void registerAdapterFactory(ComponentContext context,
             ServiceReference reference) {
-        String[] adaptables = OsgiUtil.toStringArray(reference.getProperty(ADAPTABLE_CLASSES));
-        String[] adapters = OsgiUtil.toStringArray(reference.getProperty(ADAPTER_CLASSES));
+        final String[] adaptables = OsgiUtil.toStringArray(reference.getProperty(ADAPTABLE_CLASSES));
+        final String[] adapters = OsgiUtil.toStringArray(reference.getProperty(ADAPTER_CLASSES));
 
         if (adaptables == null || adaptables.length == 0 || adapters == null
             || adapters.length == 0) {
@@ -260,6 +285,16 @@
 
         // clear the factory cache to force rebuild on next access
         factoryCache = null;
+
+        // send event
+        final EventAdmin localEA = this.getEventAdmin();
+        if ( localEA != null ) {
+            final Dictionary<String, Object> props = new Hashtable<String, Object>();
+            props.put(SlingConstants.PROPERTY_ADAPTABLE_CLASSES, adaptables);
+            props.put(SlingConstants.PROPERTY_ADAPTER_CLASSES, adapters);
+            localEA.postEvent(new Event(SlingConstants.TOPIC_ADAPTER_FACTORY_ADDED,
+                    props));
+        }
     }
 
     /**
@@ -269,9 +304,11 @@
     private void unregisterAdapterFactory(ServiceReference reference) {
         boundAdapterFactories.remove(reference);
 
-        String[] adaptables = OsgiUtil.toStringArray(reference.getProperty(ADAPTABLE_CLASSES));
+        final String[] adaptables = OsgiUtil.toStringArray(reference.getProperty(ADAPTABLE_CLASSES));
+        final String[] adapters = OsgiUtil.toStringArray(reference.getProperty(ADAPTER_CLASSES));
 
-        if (adaptables == null || adaptables.length == 0) {
+        if (adaptables == null || adaptables.length == 0 || adapters == null
+            || adapters.length == 0) {
             return;
         }
 
@@ -296,6 +333,16 @@
         if (factoriesModified) {
             factoryCache = null;
         }
+
+        // send event
+        final EventAdmin localEA = this.getEventAdmin();
+        if ( localEA != null ) {
+            final Dictionary<String, Object> props = new Hashtable<String, Object>();
+            props.put(SlingConstants.PROPERTY_ADAPTABLE_CLASSES, adaptables);
+            props.put(SlingConstants.PROPERTY_ADAPTER_CLASSES, adapters);
+            localEA.postEvent(new Event(SlingConstants.TOPIC_ADAPTER_FACTORY_ADDED,
+                    props));
+        }
     }
 
     /**
diff --git a/src/test/java/org/apache/sling/adapter/internal/AdapterManagerTest.java b/src/test/java/org/apache/sling/adapter/internal/AdapterManagerTest.java
index f5a77f5..b0d5ee5 100644
--- a/src/test/java/org/apache/sling/adapter/internal/AdapterManagerTest.java
+++ b/src/test/java/org/apache/sling/adapter/internal/AdapterManagerTest.java
@@ -34,7 +34,10 @@
 import org.jmock.integration.junit4.JUnit4Mockery;
 import org.junit.runner.RunWith;
 import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceListener;
 import org.osgi.framework.ServiceReference;
 import org.osgi.service.component.ComponentContext;
 
@@ -70,11 +73,21 @@
     /**
      * Helper method to create a mock component context
      */
-    protected ComponentContext createComponentContext() {
+    protected ComponentContext createComponentContext() throws Exception {
+        final BundleContext bundleCtx = this.context.mock(BundleContext.class);
+        final Filter filter = this.context.mock(Filter.class);
         final ComponentContext ctx = this.context.mock(ComponentContext.class);
         this.context.checking(new Expectations() {{
             allowing(ctx).locateService(with(any(String.class)), with(any(ServiceReference.class)));
             will(returnValue(new MockAdapterFactory()));
+            allowing(ctx).getBundleContext();
+            will(returnValue(bundleCtx));
+            allowing(bundleCtx).createFilter(with(any(String.class)));
+            will(returnValue(filter));
+            allowing(bundleCtx).addServiceListener(with(any(ServiceListener.class)), with(any(String.class)));
+            allowing(bundleCtx).getServiceReferences(with(any(String.class)), with(any(String.class)));
+            will(returnValue(null));
+            allowing(bundleCtx).removeServiceListener(with(any(ServiceListener.class)));
         }});
         return ctx;
     }
@@ -123,7 +136,7 @@
         assertNull("AdapterFactory cache must be null", am.getFactoryCache());
     }
 
-    @org.junit.Test public void testInitialized() {
+    @org.junit.Test public void testInitialized() throws Exception {
         am.activate(this.createComponentContext());
 
         assertNotNull("AdapterFactoryDescriptors must not be null", am.getFactories());
@@ -131,7 +144,7 @@
         assertNull("AdapterFactory cache must be null", am.getFactoryCache());
     }
 
-    @org.junit.Test public void testBindBeforeActivate() {
+    @org.junit.Test public void testBindBeforeActivate() throws Exception {
         final ServiceReference ref = createServiceReference();
         am.bindAdapterFactory(ref);
 
@@ -148,7 +161,7 @@
         assertNull("AdapterFactory cache must be null", am.getFactoryCache());
     }
 
-    @org.junit.Test public void testBindAfterActivate() {
+    @org.junit.Test public void testBindAfterActivate() throws Exception {
         am.activate(this.createComponentContext());
 
         // no cache and no factories yet
@@ -178,7 +191,7 @@
         assertNull(f.get(TestSlingAdaptable2.class.getName()));
     }
 
-    @org.junit.Test public void testAdaptBase() {
+    @org.junit.Test public void testAdaptBase() throws Exception {
         am.activate(this.createComponentContext());
 
         TestSlingAdaptable data = new TestSlingAdaptable();
@@ -192,7 +205,7 @@
         assertTrue(adapter instanceof ITestAdapter);
     }
 
-    @org.junit.Test public void testAdaptExtended() {
+    @org.junit.Test public void testAdaptExtended() throws Exception {
         am.activate(this.createComponentContext());
 
         TestSlingAdaptable2 data = new TestSlingAdaptable2();
@@ -206,7 +219,7 @@
         assertTrue(adapter instanceof ITestAdapter);
     }
 
-    @org.junit.Test public void testAdaptBase2() {
+    @org.junit.Test public void testAdaptBase2() throws Exception {
         am.activate(this.createComponentContext());
 
         TestSlingAdaptable data = new TestSlingAdaptable();
@@ -223,7 +236,7 @@
         assertTrue(adapter instanceof ITestAdapter);
     }
 
-    @org.junit.Test public void testAdaptExtended2() {
+    @org.junit.Test public void testAdaptExtended2() throws Exception {
         am.activate(this.createComponentContext());
 
         final ServiceReference ref = createServiceReference();