You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ss...@apache.org on 2017/12/21 16:24:43 UTC

[sling-org-apache-sling-testing-osgi-mock] 01/01: performance improvement: avoid endless looping through all references for lookup of dynamic and static-greedy service references and cache the lookups measured with a non-trivial project with 318 mock-based unit tests the improvement was up to 5% on test execution time

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

sseifert pushed a commit to branch feature/cache-service-referenceinfos
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-testing-osgi-mock.git

commit 567a2c67603e9d337881368e720498e03257992b
Author: sseifert <ss...@pro-vision.de>
AuthorDate: Thu Dec 21 17:24:29 2017 +0100

    performance improvement: avoid endless looping through all references for lookup of dynamic and static-greedy service references and cache the lookups
    measured with a non-trivial project with 318 mock-based unit tests the improvement was up to 5% on test execution time
    
    i'm not sure if it's worth to apply this optimization as the code gets a bit more complex and more prone to threading issues
---
 .../sling/testing/mock/osgi/MockBundleContext.java | 79 ++++++++++++++++++++--
 .../sling/testing/mock/osgi/OsgiServiceUtil.java   | 57 ----------------
 2 files changed, 73 insertions(+), 63 deletions(-)

diff --git a/src/main/java/org/apache/sling/testing/mock/osgi/MockBundleContext.java b/src/main/java/org/apache/sling/testing/mock/osgi/MockBundleContext.java
index 0340a55..08e10ba 100644
--- a/src/main/java/org/apache/sling/testing/mock/osgi/MockBundleContext.java
+++ b/src/main/java/org/apache/sling/testing/mock/osgi/MockBundleContext.java
@@ -21,9 +21,11 @@ package org.apache.sling.testing.mock.osgi;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Comparator;
 import java.util.Dictionary;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Queue;
@@ -32,12 +34,16 @@ import java.util.SortedSet;
 import java.util.TreeSet;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.ConcurrentSkipListSet;
 
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.felix.framework.FilterImpl;
+import org.apache.sling.testing.mock.osgi.OsgiMetadataUtil.OsgiMetadata;
 import org.apache.sling.testing.mock.osgi.OsgiMetadataUtil.Reference;
+import org.apache.sling.testing.mock.osgi.OsgiMetadataUtil.ReferencePolicy;
+import org.apache.sling.testing.mock.osgi.OsgiMetadataUtil.ReferencePolicyOption;
 import org.apache.sling.testing.mock.osgi.OsgiServiceUtil.ReferenceInfo;
 import org.apache.sling.testing.mock.osgi.OsgiServiceUtil.ServiceInfo;
 import org.osgi.framework.Bundle;
@@ -66,6 +72,8 @@ class MockBundleContext implements BundleContext {
 
     private final MockBundle bundle;
     private final SortedSet<MockServiceRegistration> registeredServices = new ConcurrentSkipListSet<MockServiceRegistration>();
+    private final ConcurrentMap<String,List<ReferenceInfo>> registeredServicesDynamicReferences = new ConcurrentHashMap<>();
+    private final ConcurrentMap<String,List<ReferenceInfo>> registeredServicesStaticGreedyReferences = new ConcurrentHashMap<>();
     private final Map<ServiceListener, Filter> serviceListeners = new ConcurrentHashMap<ServiceListener, Filter>();
     private final Queue<BundleListener> bundleListeners = new ConcurrentLinkedQueue<BundleListener>();
     private final ConfigurationAdmin configAdmin = new MockConfigurationAdmin();
@@ -119,7 +127,7 @@ class MockBundleContext implements BundleContext {
     public ServiceRegistration registerService(final String[] clazzes, final Object service, final Dictionary properties) {
         Dictionary<String, Object> mergedPropertes = MapMergeUtil.propertiesMergeWithOsgiMetadata(service, configAdmin, properties);
         MockServiceRegistration registration = new MockServiceRegistration(this.bundle, clazzes, service, mergedPropertes, this);
-        this.registeredServices.add(registration);
+        addToRegisteredServices(registration);
         handleRefsUpdateOnRegister(registration);
         notifyServiceListeners(ServiceEvent.REGISTERED, registration.getReference());
         return registration;
@@ -131,6 +139,65 @@ class MockBundleContext implements BundleContext {
         return registerService(clazz.getName(), factory, properties);
     }
     
+    private void addToRegisteredServices(MockServiceRegistration registration) {
+        this.registeredServices.add(registration);
+        
+        // add dynamic and static greedy references to cache
+        OsgiMetadata metadata = OsgiMetadataUtil.getMetadata(registration.getService().getClass());
+        if (metadata != null) {
+            for (Reference reference : metadata.getReferences()) {
+                if (reference.getPolicy() == ReferencePolicy.DYNAMIC) {
+                    addReferenceInfo(this.registeredServicesDynamicReferences, registration, reference);
+                }
+                else if (reference.getPolicy() == ReferencePolicy.STATIC && reference.getPolicyOption() == ReferencePolicyOption.GREEDY) {
+                    addReferenceInfo(this.registeredServicesStaticGreedyReferences, registration, reference);
+                }
+            }
+        }
+    }
+    
+    private void addReferenceInfo(ConcurrentMap<String,List<ReferenceInfo>> map, MockServiceRegistration registration, Reference reference) {
+        String serviceInterface = reference.getInterfaceType();
+        map.putIfAbsent(serviceInterface, new ArrayList<ReferenceInfo>());
+        List<ReferenceInfo> references = map.get(serviceInterface);
+        references.add(new ReferenceInfo(registration, reference));
+    }
+    
+    private void remveFromRegisteredServices(MockServiceRegistration registration) {
+        this.registeredServices.remove(registration);
+        
+        // remove from reference caches
+        removeReferenceInfos(this.registeredServicesDynamicReferences, registration);
+        removeReferenceInfos(this.registeredServicesStaticGreedyReferences, registration);
+    }
+
+    private void removeReferenceInfos(ConcurrentMap<String,List<ReferenceInfo>> map, MockServiceRegistration registration) {
+        for (List<ReferenceInfo> referenceInfos : map.values()) {
+            synchronized (referenceInfos) {
+                Iterator<ReferenceInfo> referenceInfoIterator = referenceInfos.iterator();
+                while (referenceInfoIterator.hasNext()) {
+                    ReferenceInfo referenceInfo = referenceInfoIterator.next();
+                    if (referenceInfo.getServiceRegistration() == registration) {
+                        referenceInfoIterator.remove();
+                    }
+                }
+            }
+        }
+    }
+
+    private List<ReferenceInfo> getMatchingReferences(ConcurrentMap<String,List<ReferenceInfo>> referenceInfoMaps,
+            MockServiceRegistration<?> registration) {
+
+        List<ReferenceInfo> references = new ArrayList<ReferenceInfo>();
+        for (String serviceInterface : registration.getClasses()) {
+            List<ReferenceInfo> lookedUpReferences = referenceInfoMaps.get(serviceInterface);
+            if (lookedUpReferences != null) {
+                references.addAll(lookedUpReferences);
+            }
+        }
+        return references;
+    }
+    
     /**
      * Check for already registered services that may be affected by the service registration - either
      * adding by additional optional references, or creating a conflict in the dependencies.
@@ -139,7 +206,7 @@ class MockBundleContext implements BundleContext {
     private void handleRefsUpdateOnRegister(MockServiceRegistration registration) {
         
         // handle DYNAMIC references to this registration
-        List<ReferenceInfo> affectedDynamicReferences = OsgiServiceUtil.getMatchingDynamicReferences(registeredServices, registration);
+        List<ReferenceInfo> affectedDynamicReferences = getMatchingReferences(this.registeredServicesDynamicReferences, registration);
         for (ReferenceInfo referenceInfo : affectedDynamicReferences) {
             Reference reference = referenceInfo.getReference();
             if (reference.matchesTargetFilter(registration.getReference())) {
@@ -160,7 +227,7 @@ class MockBundleContext implements BundleContext {
         }
 
         // handle STATIC+GREEDY references to this registration
-        List<ReferenceInfo> affectedStaticGreedyReferences = OsgiServiceUtil.getMatchingStaticGreedyReferences(registeredServices, registration);
+        List<ReferenceInfo> affectedStaticGreedyReferences = getMatchingReferences(this.registeredServicesStaticGreedyReferences, registration);
         for (ReferenceInfo referenceInfo : affectedStaticGreedyReferences) {
             Reference reference = referenceInfo.getReference();
             switch (reference.getCardinality()) {
@@ -179,7 +246,7 @@ class MockBundleContext implements BundleContext {
     }
     
     void unregisterService(MockServiceRegistration registration) {
-        this.registeredServices.remove(registration);
+        remveFromRegisteredServices(registration);
         handleRefsUpdateOnUnregister(registration);
         notifyServiceListeners(ServiceEvent.UNREGISTERING, registration.getReference());
     }
@@ -218,7 +285,7 @@ class MockBundleContext implements BundleContext {
     private void handleRefsUpdateOnUnregister(MockServiceRegistration registration) {
 
         // handle DYNAMIC references to this registration
-        List<ReferenceInfo> affectedDynamicReferences = OsgiServiceUtil.getMatchingDynamicReferences(registeredServices, registration);
+        List<ReferenceInfo> affectedDynamicReferences = getMatchingReferences(this.registeredServicesDynamicReferences, registration);
         for (ReferenceInfo referenceInfo : affectedDynamicReferences) {
             Reference reference = referenceInfo.getReference();
             if (reference.matchesTargetFilter(registration.getReference())) {
@@ -241,7 +308,7 @@ class MockBundleContext implements BundleContext {
         }
 
         // handle STATIC+GREEDY references to this registration
-        List<ReferenceInfo> affectedStaticGreedyReferences = OsgiServiceUtil.getMatchingStaticGreedyReferences(registeredServices, registration);
+        List<ReferenceInfo> affectedStaticGreedyReferences = getMatchingReferences(this.registeredServicesStaticGreedyReferences, registration);
         for (ReferenceInfo referenceInfo : affectedStaticGreedyReferences) {
             Reference reference = referenceInfo.getReference();
             switch (reference.getCardinality()) {
diff --git a/src/main/java/org/apache/sling/testing/mock/osgi/OsgiServiceUtil.java b/src/main/java/org/apache/sling/testing/mock/osgi/OsgiServiceUtil.java
index b3fd20c..9bdbd4a 100644
--- a/src/main/java/org/apache/sling/testing/mock/osgi/OsgiServiceUtil.java
+++ b/src/main/java/org/apache/sling/testing/mock/osgi/OsgiServiceUtil.java
@@ -27,15 +27,12 @@ import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.SortedSet;
 
 import org.apache.commons.lang3.StringUtils;
 import org.apache.felix.scr.impl.inject.Annotations;
 import org.apache.sling.testing.mock.osgi.OsgiMetadataUtil.FieldCollectionType;
 import org.apache.sling.testing.mock.osgi.OsgiMetadataUtil.OsgiMetadata;
 import org.apache.sling.testing.mock.osgi.OsgiMetadataUtil.Reference;
-import org.apache.sling.testing.mock.osgi.OsgiMetadataUtil.ReferencePolicy;
-import org.apache.sling.testing.mock.osgi.OsgiMetadataUtil.ReferencePolicyOption;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceReference;
@@ -621,60 +618,6 @@ final class OsgiServiceUtil {
         return matchingServices;
     }
 
-    /**
-     * Collects all references of any registered service that match with any of the exported interfaces of the given service registration
-     * and are defined as DYNAMIC.
-     * @param registeredServices Registered Services
-     * @param registration Service registration
-     * @return List of references
-     */
-    public static List<ReferenceInfo> getMatchingDynamicReferences(SortedSet<MockServiceRegistration> registeredServices,
-            MockServiceRegistration<?> registration) {
-        List<ReferenceInfo> references = new ArrayList<ReferenceInfo>();
-        for (MockServiceRegistration existingRegistration : registeredServices) {
-            OsgiMetadata metadata = OsgiMetadataUtil.getMetadata(existingRegistration.getService().getClass());
-            if (metadata != null) {
-                for (Reference reference : metadata.getReferences()) {
-                    if (reference.getPolicy() == ReferencePolicy.DYNAMIC) {
-                        for (String serviceInterface : registration.getClasses()) {
-                            if (StringUtils.equals(serviceInterface, reference.getInterfaceType())) {
-                                references.add(new ReferenceInfo(existingRegistration, reference));
-                            }
-                        }
-                    }
-                }
-            }
-        }
-        return references;
-    }
-            
-    /**
-     * Collects all references of any registered service that match with any of the exported interfaces of the given service registration
-     * and are defined as STATIC + GREEDY.
-     * @param registeredServices Registered Services
-     * @param registration Service registration
-     * @return List of references
-     */
-    public static List<ReferenceInfo> getMatchingStaticGreedyReferences(SortedSet<MockServiceRegistration> registeredServices,
-            MockServiceRegistration<?> registration) {
-        List<ReferenceInfo> references = new ArrayList<ReferenceInfo>();
-        for (MockServiceRegistration existingRegistration : registeredServices) {
-            OsgiMetadata metadata = OsgiMetadataUtil.getMetadata(existingRegistration.getService().getClass());
-            if (metadata != null) {
-                for (Reference reference : metadata.getReferences()) {
-                    if (reference.getPolicy() == ReferencePolicy.STATIC && reference.getPolicyOption() == ReferencePolicyOption.GREEDY) {
-                        for (String serviceInterface : registration.getClasses()) {
-                            if (StringUtils.equals(serviceInterface, reference.getInterfaceType())) {
-                                references.add(new ReferenceInfo(existingRegistration, reference));
-                            }
-                        }
-                    }
-                }
-            }
-        }
-        return references;
-    }
-            
     static class ServiceInfo {
 
         private final Object serviceInstance;

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.