This is an automated email from the ASF dual-hosted git repository.

cziegeler pushed a commit to branch master
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-adapter.git


The following commit(s) were added to refs/heads/master by this push:
     new a03221c  SLING-10885 : Improve adapter factory handling
a03221c is described below

commit a03221cf0b9bdbaaf33b8104a54e5116371d3ba8
Author: Carsten Ziegeler <[email protected]>
AuthorDate: Fri Oct 29 13:43:48 2021 +0200

    SLING-10885 : Improve adapter factory handling
---
 pom.xml                                            |  6 +-
 .../adapter/internal/AdapterFactoryDescriptor.java | 10 ++-
 .../sling/adapter/internal/AdapterManagerImpl.java | 89 +++++++++-------------
 .../sling/adapter/internal/AdapterManagerTest.java | 50 +++++++++++-
 4 files changed, 97 insertions(+), 58 deletions(-)

diff --git a/pom.xml b/pom.xml
index 98b1359..716a15a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -96,9 +96,9 @@
             <artifactId>slf4j-api</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.util.converter</artifactId>
-            <version>1.0.0</version>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.converter</artifactId>
+            <version>1.0.18</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
diff --git 
a/src/main/java/org/apache/sling/adapter/internal/AdapterFactoryDescriptor.java 
b/src/main/java/org/apache/sling/adapter/internal/AdapterFactoryDescriptor.java
index be1bf22..a9ed454 100644
--- 
a/src/main/java/org/apache/sling/adapter/internal/AdapterFactoryDescriptor.java
+++ 
b/src/main/java/org/apache/sling/adapter/internal/AdapterFactoryDescriptor.java
@@ -33,13 +33,17 @@ public class AdapterFactoryDescriptor {
 
     private final String[] adapters;
 
+    private final String[] adaptables;
+
     private volatile ServiceRegistration<Adaption> adaption;
 
     public AdapterFactoryDescriptor(
             final AdapterFactory factory,
-            final String[] adapters) {
+            final String[] adapters,
+            final String[] adaptables) {
         this.factory = factory;
         this.adapters = adapters;
+        this.adaptables = adaptables;
     }
 
     public AdapterFactory getFactory() {
@@ -50,6 +54,10 @@ public class AdapterFactoryDescriptor {
         return adapters;
     }
 
+    public String[] getAdaptables() {
+        return adaptables;
+    }
+
     public ServiceRegistration<Adaption> getAdaption() {
         return adaption;
     }
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 16a661b..8053a92 100644
--- a/src/main/java/org/apache/sling/adapter/internal/AdapterManagerImpl.java
+++ b/src/main/java/org/apache/sling/adapter/internal/AdapterManagerImpl.java
@@ -38,7 +38,6 @@ import org.apache.sling.api.adapter.AdapterManager;
 import org.apache.sling.api.adapter.SlingAdaptable;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceReference;
-import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.Deactivate;
@@ -119,7 +118,7 @@ public class AdapterManagerImpl implements AdapterManager {
 
         if (descList != null && descList.size() > 0) {
             for (AdapterFactoryDescriptor desc : descList) {
-                final AdapterFactory factory = desc == null ? null : 
desc.getFactory();
+                final AdapterFactory factory = desc.getFactory();
 
                 // have the factory adapt the adaptable if the factory exists
                 if (factory != null) {
@@ -167,7 +166,7 @@ public class AdapterManagerImpl implements AdapterManager {
     /**
      * Bind a new adapter factory.
      */
-    @Reference(name="AdapterFactory", service=AdapterFactory.class,
+    @Reference(service=AdapterFactory.class, updated = "updatedAdapterFactory",
             cardinality=ReferenceCardinality.MULTIPLE, 
policy=ReferencePolicy.DYNAMIC)
     protected void bindAdapterFactory(final AdapterFactory factory, final 
ServiceReference<AdapterFactory> reference) {
         registerAdapterFactory(factory, reference);
@@ -180,6 +179,14 @@ public class AdapterManagerImpl implements AdapterManager {
         unregisterAdapterFactory(reference);
     }
 
+    /**
+     * Modify a adapter factory.
+     */
+    protected void updatedAdapterFactory(final AdapterFactory factory, final 
ServiceReference<AdapterFactory> reference) {
+        unregisterAdapterFactory(reference);
+        registerAdapterFactory(factory, reference);
+    }
+
     // ---------- unit testing stuff only 
--------------------------------------
 
     /**
@@ -228,7 +235,7 @@ public class AdapterManagerImpl implements AdapterManager {
             }
         }
 
-        final AdapterFactoryDescriptor factoryDesc = new 
AdapterFactoryDescriptor(factory, adapters);
+        final AdapterFactoryDescriptor factoryDesc = new 
AdapterFactoryDescriptor(factory, adapters, adaptables);
 
         for (final String adaptable : adaptables) {
             AdapterFactoryDescriptorMap adfMap = null;
@@ -290,63 +297,43 @@ public class AdapterManagerImpl implements AdapterManager 
{
      * <code>reference</code> from the registry.
      */
     private void unregisterAdapterFactory(final 
ServiceReference<AdapterFactory> reference) {
-        final Converter converter = Converters.standardConverter();
-        final String[] adaptables = 
converter.convert(reference.getProperty(ADAPTABLE_CLASSES)).to(String[].class);
-        final String[] adapters = 
converter.convert(reference.getProperty(ADAPTER_CLASSES)).to(String[].class);
-
-        if (adaptables == null || adaptables.length == 0 || adapters == null 
|| adapters.length == 0) {
-            return;
+        final List<AdapterFactoryDescriptorMap> list = new ArrayList<>();
+        synchronized ( this.descriptors ) {
+            for(final AdapterFactoryDescriptorMap map : 
this.descriptors.values()) {
+                list.add(map);
+            }
         }
-
-        boolean factoriesModified = false;
-        AdapterFactoryDescriptorMap adfMap = null;
-
         AdapterFactoryDescriptor removedDescriptor = null;
-        for (final String adaptable : adaptables) {
-            synchronized ( this.descriptors ) {
-                adfMap = this.descriptors.get(adaptable);
-            }
-            if (adfMap != null) {
-                synchronized ( adfMap ) {
-                    AdapterFactoryDescriptor factoryDesc = 
adfMap.remove(reference);
-                    if (factoryDesc != null) {
-                        factoriesModified = true;
-                        // A single ServiceReference should correspond to a 
single Adaption service being registered
-                        // Since the code paths above does not fully guarantee 
it though, let's keep this check in place
-                        if (removedDescriptor != null && removedDescriptor != 
factoryDesc) {
-                            log.error("When unregistering reference {} got 
duplicate service descriptors {} and {}. Unregistration of {} services may be 
incomplete.",
-                                    new Object[] { reference, 
removedDescriptor, factoryDesc, Adaption.class.getName()} );
-                        }
-                        removedDescriptor = factoryDesc;
-                    }
+        for(final AdapterFactoryDescriptorMap map : list) {
+            synchronized (map ) {
+                final AdapterFactoryDescriptor factoryDesc = 
map.remove(reference);
+                if (factoryDesc != null) {
+                    removedDescriptor = factoryDesc;
                 }
             }
         }
 
-        // only remove cache if some adapter factories have actually been
-        // removed
-        if (factoriesModified) {
-            this.factoryCache.clear();
-        }
-
         // unregister adaption
         if (removedDescriptor != null) {
+            // only remove cache if some adapter factories have actually been
+            // removed
+            this.factoryCache.clear();
+
             removedDescriptor.getAdaption().unregister();
             if (log.isDebugEnabled()) {
                 log.debug("Unregistered service {} with {} : {} and {} : {}", 
new Object[] { Adaption.class.getName(),
-                        SlingConstants.PROPERTY_ADAPTABLE_CLASSES, 
Arrays.toString(adaptables),
-                        SlingConstants.PROPERTY_ADAPTER_CLASSES, 
Arrays.toString(adapters) });
+                        SlingConstants.PROPERTY_ADAPTABLE_CLASSES, 
Arrays.toString(removedDescriptor.getAdaptables()),
+                        SlingConstants.PROPERTY_ADAPTER_CLASSES, 
Arrays.toString(removedDescriptor.getAdapters()) });
             }
-        }
 
-        // send event
-        final EventAdmin localEA = this.eventAdmin;
-        if ( localEA != null ) {
-            final Dictionary<String, Object> props = new Hashtable<>();
-            props.put(SlingConstants.PROPERTY_ADAPTABLE_CLASSES, adaptables);
-            props.put(SlingConstants.PROPERTY_ADAPTER_CLASSES, adapters);
-            localEA.postEvent(new 
Event(SlingConstants.TOPIC_ADAPTER_FACTORY_REMOVED,
-                    props));
+            // send event
+            final EventAdmin localEA = this.eventAdmin;
+            if ( localEA != null ) {
+                final Dictionary<String, Object> props = new Hashtable<>();
+                props.put(SlingConstants.PROPERTY_ADAPTABLE_CLASSES, 
removedDescriptor.getAdaptables());
+                props.put(SlingConstants.PROPERTY_ADAPTER_CLASSES, 
removedDescriptor.getAdapters());
+                localEA.postEvent(new 
Event(SlingConstants.TOPIC_ADAPTER_FACTORY_REMOVED, props));
+            }
         }
     }
 
@@ -448,12 +435,8 @@ public class AdapterManagerImpl implements AdapterManager {
         // for each target class copy the entry to dest and put it in the list 
or create the list
         for (Map.Entry<String, List<AdapterFactoryDescriptor>> entry : 
scMap.entrySet()) {
 
-            List<AdapterFactoryDescriptor> factoryDescriptors = 
dest.get(entry.getKey());
+            final List<AdapterFactoryDescriptor> factoryDescriptors = 
dest.computeIfAbsent(entry.getKey(), id -> new ArrayList<>());
 
-            if (factoryDescriptors == null) {
-                factoryDescriptors = new ArrayList<>();
-                dest.put(entry.getKey(), factoryDescriptors);
-            }
             for (AdapterFactoryDescriptor descriptor : entry.getValue()) {
                 factoryDescriptors.add(descriptor);
             }
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 8b35ddf..4b047b2 100644
--- a/src/test/java/org/apache/sling/adapter/internal/AdapterManagerTest.java
+++ b/src/test/java/org/apache/sling/adapter/internal/AdapterManagerTest.java
@@ -36,6 +36,7 @@ import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.packageadmin.ExportedPackage;
 import org.osgi.service.packageadmin.PackageAdmin;
 
@@ -92,8 +93,30 @@ public class AdapterManagerTest {
     }
 
     @Test
-    public void testBindAfterActivate() throws Exception {
+    public void testInvalidRegistrations() throws Exception {
+        ServiceReference<AdapterFactory> ref = createServiceReference(0, null, 
new String[] {TestAdapter.class.getName()});
+        am.bindAdapterFactory(Mockito.mock(AdapterFactory.class), ref);
+        assertTrue("AdapterFactoryDescriptors must be empty", 
am.getFactories().isEmpty());
+
+        ref = createServiceReference(0, new String[0], new String[] 
{TestAdapter.class.getName()});
+        am.bindAdapterFactory(Mockito.mock(AdapterFactory.class), ref);
+        assertTrue("AdapterFactoryDescriptors must be empty", 
am.getFactories().isEmpty());
+
+        ref = createServiceReference(0, new String[] 
{TestSlingAdaptable.class.getName()}, null);
+        am.bindAdapterFactory(Mockito.mock(AdapterFactory.class), ref);
+        assertTrue("AdapterFactoryDescriptors must be empty", 
am.getFactories().isEmpty());
+
+        ref = createServiceReference(0, new String[] 
{TestSlingAdaptable.class.getName()}, new String[0]);
+        am.bindAdapterFactory(Mockito.mock(AdapterFactory.class), ref);
+        assertTrue("AdapterFactoryDescriptors must be empty", 
am.getFactories().isEmpty());
+    }
+
+    @Test
+    public void testBindUnbind() throws Exception {
         final ServiceReference<AdapterFactory> ref = createServiceReference();
+        final ServiceRegistration<Adaption> registration = 
Mockito.mock(ServiceRegistration.class);
+        
Mockito.when(ref.getBundle().getBundleContext().registerService(Mockito.eq(Adaption.class),
 
+            Mockito.eq(AdaptionImpl.INSTANCE), 
Mockito.any())).thenReturn(registration);
         am.bindAdapterFactory(Mockito.mock(AdapterFactory.class), ref);
 
         // check that a service is registered
@@ -103,6 +126,7 @@ public class AdapterManagerTest {
         // expect the factory, but cache is empty
         assertNotNull("AdapterFactoryDescriptors must not be null", 
am.getFactories());
         assertEquals("AdapterFactoryDescriptors must contain one entry", 1, 
am.getFactories().size());
+        assertEquals(1, 
am.getFactories().get(TestSlingAdaptable.class.getName()).size());
         assertTrue("AdapterFactory cache must be empty", 
am.getFactoryCache().isEmpty());
 
         Map<String, AdapterFactoryDescriptorMap> f = am.getFactories();
@@ -117,6 +141,30 @@ public class AdapterManagerTest {
         assertEquals(ITestAdapter.class.getName(), afd.getAdapters()[0]);
 
         assertNull(f.get(TestSlingAdaptable2.class.getName()));
+
+        Mockito.verify(registration, Mockito.never()).unregister();
+        am.unbindAdapterFactory(ref);
+        Mockito.verify(registration).unregister();
+        
assertTrue(am.getFactories().get(TestSlingAdaptable.class.getName()).isEmpty());
+        assertTrue("AdapterFactory cache must be empty", 
am.getFactoryCache().isEmpty());
+    }
+
+    @Test
+    public void testBindModifiedUnbind() throws Exception {
+        ServiceReference<AdapterFactory> ref = createServiceReference();
+        final ServiceRegistration<Adaption> registration = 
Mockito.mock(ServiceRegistration.class);
+        
Mockito.when(ref.getBundle().getBundleContext().registerService(Mockito.eq(Adaption.class),
 
+            Mockito.eq(AdaptionImpl.INSTANCE), 
Mockito.any())).thenReturn(registration);
+        am.bindAdapterFactory(Mockito.mock(AdapterFactory.class), ref);
+
+        assertEquals("AdapterFactoryDescriptors must contain one entry", 1, 
am.getFactories().size());
+        assertEquals(1, 
am.getFactories().get(TestSlingAdaptable.class.getName()).size());
+
+        
Mockito.when(ref.getProperty(AdapterFactory.ADAPTABLE_CLASSES)).thenReturn(new 
String[] {TestSlingAdaptable2.class.getName()});
+        am.updatedAdapterFactory(Mockito.mock(AdapterFactory.class), ref);
+        assertEquals("AdapterFactoryDescriptors must contain two entries", 2, 
am.getFactories().size());
+        assertEquals(0, 
am.getFactories().get(TestSlingAdaptable.class.getName()).size());
+        assertEquals(1, 
am.getFactories().get(TestSlingAdaptable2.class.getName()).size());
     }
 
     @Test

Reply via email to