You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aries.apache.org by cs...@apache.org on 2018/02/05 09:45:03 UTC

[1/3] aries-rsa git commit: [ARIES-1771] Support service changes, let TopologyManager handle the closing of ExportRegistrations

Repository: aries-rsa
Updated Branches:
  refs/heads/master 094996dc0 -> 55e2c1cb4


[ARIES-1771] Support service changes, let TopologyManager handle the closing of ExportRegistrations


Project: http://git-wip-us.apache.org/repos/asf/aries-rsa/repo
Commit: http://git-wip-us.apache.org/repos/asf/aries-rsa/commit/1ad152b4
Tree: http://git-wip-us.apache.org/repos/asf/aries-rsa/tree/1ad152b4
Diff: http://git-wip-us.apache.org/repos/asf/aries-rsa/diff/1ad152b4

Branch: refs/heads/master
Commit: 1ad152b46616f4394177588cf06a758913671a77
Parents: 094996d
Author: Christian Schneider <cs...@adobe.com>
Authored: Fri Feb 2 18:35:44 2018 +0100
Committer: Christian Schneider <cs...@adobe.com>
Committed: Fri Feb 2 18:35:44 2018 +0100

----------------------------------------------------------------------
 .../rsa/itests/felix/rsa/TestRSAListener.java   |   2 +
 parent/pom.xml                                  |  12 +-
 .../aries/rsa/core/ExportRegistrationImpl.java  |  30 +--
 .../aries/rsa/core/RemoteServiceAdminCore.java  |  20 --
 .../core/DistributionProviderTrackerTest.java   |   7 -
 topology-manager/pom.xml                        |   6 -
 .../aries/rsa/topologymanager/Activator.java    |  76 +-------
 .../EndpointEventListenerTracker.java           |  39 ++++
 .../EndpointListenerTracker.java                |  43 +++++
 .../exporter/EndpointListenerNotifier.java      |  27 +--
 .../exporter/EndpointRepository.java            | 147 ---------------
 .../exporter/ServiceExportsRepository.java      | 122 ++++++++++++
 .../exporter/TopologyManagerExport.java         | 189 +++++++++++--------
 .../exporter/EndpointListenerNotifierTest.java  |  29 +--
 .../exporter/EndpointRepositoryTest.java        |  74 --------
 .../exporter/TopologyManagerExportTest.java     |  93 +++++----
 16 files changed, 426 insertions(+), 490 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/1ad152b4/itests/felix/src/test/java/org/apache/aries/rsa/itests/felix/rsa/TestRSAListener.java
----------------------------------------------------------------------
diff --git a/itests/felix/src/test/java/org/apache/aries/rsa/itests/felix/rsa/TestRSAListener.java b/itests/felix/src/test/java/org/apache/aries/rsa/itests/felix/rsa/TestRSAListener.java
index 8a0c9f9..0371787 100644
--- a/itests/felix/src/test/java/org/apache/aries/rsa/itests/felix/rsa/TestRSAListener.java
+++ b/itests/felix/src/test/java/org/apache/aries/rsa/itests/felix/rsa/TestRSAListener.java
@@ -71,6 +71,8 @@ public class TestRSAListener extends RsaTestBase implements RemoteServiceAdminLi
         serviceBundle.start();
         assertEvent(RemoteServiceAdminEvent.EXPORT_REGISTRATION);
         
+        Thread.sleep(1000);
+        
         serviceBundle.stop();
         assertEvent(RemoteServiceAdminEvent.EXPORT_UNREGISTRATION);
 

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/1ad152b4/parent/pom.xml
----------------------------------------------------------------------
diff --git a/parent/pom.xml b/parent/pom.xml
index 43a4c07..09e9cc7 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -80,6 +80,11 @@
               <artifactId>hamcrest-all</artifactId>
               <version>1.3</version>
         </dependency>
+        <dependency>
+             <groupId>com.shazam</groupId>
+             <artifactId>shazamcrest</artifactId>
+             <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <dependencyManagement>
@@ -124,7 +129,12 @@
                 <artifactId>hawtbuf-proto</artifactId>
                 <version>${hawtbuf.version}</version>
             </dependency>
-
+            <dependency>
+                <groupId>com.shazam</groupId>
+                <artifactId>shazamcrest</artifactId>
+                <version>0.11</version>
+                <scope>test</scope>
+            </dependency>
             <dependency>
                 <groupId>junit</groupId>
                 <artifactId>junit</artifactId>

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/1ad152b4/rsa/src/main/java/org/apache/aries/rsa/core/ExportRegistrationImpl.java
----------------------------------------------------------------------
diff --git a/rsa/src/main/java/org/apache/aries/rsa/core/ExportRegistrationImpl.java b/rsa/src/main/java/org/apache/aries/rsa/core/ExportRegistrationImpl.java
index bb8ee04..81bb859 100644
--- a/rsa/src/main/java/org/apache/aries/rsa/core/ExportRegistrationImpl.java
+++ b/rsa/src/main/java/org/apache/aries/rsa/core/ExportRegistrationImpl.java
@@ -21,7 +21,6 @@ package org.apache.aries.rsa.core;
 import java.io.Closeable;
 import java.io.IOException;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
 
@@ -40,7 +39,7 @@ public class ExportRegistrationImpl implements ExportRegistration {
     private static final Logger LOG = LoggerFactory.getLogger(ExportRegistrationImpl.class);
 
     private final CloseHandler closeHandler;
-    private final ExportReferenceImpl exportReference;
+    private ExportReferenceImpl exportReference;
     private final Closeable server;
     private final Throwable exception;
 
@@ -138,14 +137,17 @@ public class ExportRegistrationImpl implements ExportRegistration {
             instanceCount--;
             if (instanceCount <= 0) {
                 LOG.debug("really closing ExportRegistration now!");
+                closeServer();
+            }
+        }
+    }
 
-                if (server != null) {
-                    try {
-                        server.close();
-                    } catch (IOException e) {
-                        LOG.warn("Error closing ExportRegistration", e);
-                    }
-                }
+    private void closeServer() {
+        if (server != null) {
+            try {
+                server.close();
+            } catch (IOException e) {
+                LOG.warn("Error closing ExportRegistration", e);
             }
         }
     }
@@ -175,11 +177,13 @@ public class ExportRegistrationImpl implements ExportRegistration {
 
     @Override
     public EndpointDescription update(Map<String, ?> properties) {
-        Map<String, Object> newProps = new HashMap<String, Object>(getExportReference().getExportedEndpoint().getProperties()); 
-        for (String key : properties.keySet()) {
-            newProps.put(key, properties.get(key));
+        if (getExportReference() == null) {
+            return null;
         }
+        ServiceReference<?> sref = getExportReference().getExportedService();
+        EndpointDescription epd = new EndpointDescription(sref, properties);
+        exportReference = new ExportReferenceImpl(sref, epd);
         this.sender.notifyUpdate(this.getExportReference());
-        return new EndpointDescription(newProps);
+        return epd;
     }
 }

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/1ad152b4/rsa/src/main/java/org/apache/aries/rsa/core/RemoteServiceAdminCore.java
----------------------------------------------------------------------
diff --git a/rsa/src/main/java/org/apache/aries/rsa/core/RemoteServiceAdminCore.java b/rsa/src/main/java/org/apache/aries/rsa/core/RemoteServiceAdminCore.java
index e2a574b..98fca14 100644
--- a/rsa/src/main/java/org/apache/aries/rsa/core/RemoteServiceAdminCore.java
+++ b/rsa/src/main/java/org/apache/aries/rsa/core/RemoteServiceAdminCore.java
@@ -39,8 +39,6 @@ import org.apache.aries.rsa.util.EndpointHelper;
 import org.apache.aries.rsa.util.StringPlus;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceEvent;
 import org.osgi.framework.ServiceListener;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
@@ -93,24 +91,6 @@ public class RemoteServiceAdminCore implements RemoteServiceAdmin {
                 removeImportRegistration((ImportRegistrationImpl) importReg);
             }
         };
-        // listen for exported services being unregistered so we can close the export
-        createExportedServicesListener();
-    }
-
-    protected void createExportedServicesListener() {
-        this.exportedServiceListener = new ServiceListener() {
-            public void serviceChanged(ServiceEvent event) {
-                if (event.getType() == ServiceEvent.UNREGISTERING) {
-                    removeServiceExports(event.getServiceReference());
-                }
-            }
-        };
-        try {
-            String filter = "(" + RemoteConstants.SERVICE_EXPORTED_INTERFACES + "=*)";
-            bctx.addServiceListener(exportedServiceListener, filter);
-        } catch (InvalidSyntaxException ise) {
-            throw new RuntimeException(ise); // can never happen
-        }
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/1ad152b4/rsa/src/test/java/org/apache/aries/rsa/core/DistributionProviderTrackerTest.java
----------------------------------------------------------------------
diff --git a/rsa/src/test/java/org/apache/aries/rsa/core/DistributionProviderTrackerTest.java b/rsa/src/test/java/org/apache/aries/rsa/core/DistributionProviderTrackerTest.java
index ca71802..1468b8b 100644
--- a/rsa/src/test/java/org/apache/aries/rsa/core/DistributionProviderTrackerTest.java
+++ b/rsa/src/test/java/org/apache/aries/rsa/core/DistributionProviderTrackerTest.java
@@ -29,7 +29,6 @@ import org.osgi.framework.Filter;
 import org.osgi.framework.FrameworkUtil;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceFactory;
-import org.osgi.framework.ServiceListener;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.remoteserviceadmin.RemoteConstants;
@@ -59,9 +58,6 @@ public class DistributionProviderTrackerTest {
                                                 EasyMock.isA(Dictionary.class)))
             .andReturn(rsaReg).atLeastOnce();
 
-        context.addServiceListener(EasyMock.isA(ServiceListener.class), EasyMock.isA(String.class));
-        EasyMock.expectLastCall();
-        
         final BundleContext apiContext = c.createMock(BundleContext.class);
         c.replay();
         DistributionProviderTracker tracker = new DistributionProviderTracker(context) {
@@ -100,9 +96,6 @@ public class DistributionProviderTrackerTest {
                                                 EasyMock.isA(Dictionary.class)))
             .andReturn(rsaReg).atLeastOnce();
 
-        context.addServiceListener(EasyMock.isA(ServiceListener.class), EasyMock.isA(String.class));
-        EasyMock.expectLastCall();
-
         final BundleContext apiContext = c.createMock(BundleContext.class);
         c.replay();
         DistributionProviderTracker tracker = new DistributionProviderTracker(context) {

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/1ad152b4/topology-manager/pom.xml
----------------------------------------------------------------------
diff --git a/topology-manager/pom.xml b/topology-manager/pom.xml
index cc63441..43a0582 100644
--- a/topology-manager/pom.xml
+++ b/topology-manager/pom.xml
@@ -42,12 +42,6 @@
             <artifactId>org.apache.aries.rsa.spi</artifactId>
             <scope>provided</scope>
         </dependency>
-        <dependency>
-            <groupId>org.hamcrest</groupId>
-            <artifactId>hamcrest-all</artifactId>
-            <version>1.3</version>
-            <scope>test</scope>
-        </dependency>
     </dependencies>
 
 </project>

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/1ad152b4/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/Activator.java
----------------------------------------------------------------------
diff --git a/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/Activator.java b/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/Activator.java
index 70d1111..5efc1c5 100644
--- a/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/Activator.java
+++ b/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/Activator.java
@@ -26,9 +26,7 @@ import java.util.concurrent.TimeUnit;
 
 import org.apache.aries.rsa.spi.ExportPolicy;
 import org.apache.aries.rsa.topologymanager.exporter.DefaultExportPolicy;
-import org.apache.aries.rsa.topologymanager.exporter.EndpointListenerAdapter;
 import org.apache.aries.rsa.topologymanager.exporter.EndpointListenerNotifier;
-import org.apache.aries.rsa.topologymanager.exporter.EndpointRepository;
 import org.apache.aries.rsa.topologymanager.exporter.TopologyManagerExport;
 import org.apache.aries.rsa.topologymanager.importer.TopologyManagerImport;
 import org.apache.aries.rsa.topologymanager.importer.local.EndpointListenerManager;
@@ -39,8 +37,6 @@ import org.osgi.framework.FrameworkUtil;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceEvent;
 import org.osgi.framework.ServiceReference;
-import org.osgi.service.remoteserviceadmin.EndpointEventListener;
-import org.osgi.service.remoteserviceadmin.EndpointListener;
 import org.osgi.service.remoteserviceadmin.RemoteConstants;
 import org.osgi.service.remoteserviceadmin.RemoteServiceAdmin;
 import org.osgi.util.tracker.ServiceTracker;
@@ -48,7 +44,6 @@ import org.osgi.util.tracker.ServiceTrackerCustomizer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-@SuppressWarnings("deprecation")
 public class Activator implements BundleActivator {
     public static final String RSA_EXPORT_POLICY_FILTER = "rsa.export.policy.filter";
     static final String DOSGI_SERVICES = "(" + RemoteConstants.SERVICE_EXPORTED_INTERFACES + "=*)";
@@ -56,13 +51,13 @@ public class Activator implements BundleActivator {
 
     private TopologyManagerExport exportManager;
     private TopologyManagerImport importManager;
-    private EndpointListenerNotifier notifier;
+    EndpointListenerNotifier notifier;
     private ServiceTracker<RemoteServiceAdmin, RemoteServiceAdmin> rsaTracker;
     private ThreadPoolExecutor exportExecutor;
     
-    private ServiceTracker<EndpointListener, EndpointListener> epListenerTracker;
     private ServiceTracker<ExportPolicy, ExportPolicy> policyTracker;
     private EndpointListenerManager endpointListenerManager;
+    private EndpointListenerTracker epListenerTracker;
     private EndpointEventListenerTracker epeListenerTracker;
 
     public void start(final BundleContext bc) throws Exception {
@@ -103,13 +98,11 @@ public class Activator implements BundleActivator {
 
     public void doStart(final BundleContext bc, ExportPolicy policy) {
         LOG.debug("TopologyManager: start()");
-        EndpointRepository endpointRepo = new EndpointRepository();
-        notifier = new EndpointListenerNotifier(endpointRepo);
-        epListenerTracker = new EndpointListenerTracker(bc);
-        epeListenerTracker = new EndpointEventListenerTracker(bc);
-        endpointRepo.setNotifier(notifier);
+        notifier = new EndpointListenerNotifier();
         exportExecutor = new ThreadPoolExecutor(5, 10, 50, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
-        exportManager = new TopologyManagerExport(endpointRepo, exportExecutor, policy);
+        exportManager = new TopologyManagerExport(notifier, exportExecutor, policy);
+        epListenerTracker = new EndpointListenerTracker(bc, exportManager);
+        epeListenerTracker = new EndpointEventListenerTracker(bc, exportManager);
         importManager = new TopologyManagerImport(bc);
         endpointListenerManager = new EndpointListenerManager(bc, importManager);
         endpointListenerManager.start();
@@ -151,63 +144,6 @@ public class Activator implements BundleActivator {
         }
     }
     
-    private final class EndpointListenerTracker extends ServiceTracker<EndpointListener, EndpointListener> {
-        private EndpointListenerTracker(BundleContext context) {
-            super(context, EndpointListener.class, null);
-        }
-
-        @Override
-        public EndpointListener addingService(ServiceReference<EndpointListener> reference) {
-            EndpointListener listener = super.addingService(reference);
-            EndpointListenerAdapter adapter = new EndpointListenerAdapter(listener);
-            notifier.add(adapter, EndpointListenerNotifier.filtersFromEL(reference));
-            return listener;
-        }
-
-        @Override
-        public void modifiedService(ServiceReference<EndpointListener> reference,
-                                    EndpointListener listener) {
-            super.modifiedService(reference, listener);
-            EndpointListenerAdapter adapter = new EndpointListenerAdapter(listener);
-            notifier.add(adapter, EndpointListenerNotifier.filtersFromEL(reference));
-        }
-
-        @Override
-        public void removedService(ServiceReference<EndpointListener> reference,
-                                   EndpointListener listener) {
-            EndpointListenerAdapter adapter = new EndpointListenerAdapter(listener);
-            notifier.remove(adapter);
-            super.removedService(reference, listener);
-        }
-    }
-    
-    private final class EndpointEventListenerTracker extends ServiceTracker<EndpointEventListener, EndpointEventListener> {
-        private EndpointEventListenerTracker(BundleContext context) {
-            super(context, EndpointEventListener.class, null);
-        }
-
-        @Override
-        public EndpointEventListener addingService(ServiceReference<EndpointEventListener> reference) {
-            EndpointEventListener listener = super.addingService(reference);
-            notifier.add(listener, EndpointListenerNotifier.filtersFromEEL(reference));
-            return listener;
-        }
-
-        @Override
-        public void modifiedService(ServiceReference<EndpointEventListener> reference,
-                EndpointEventListener listener) {
-            notifier.add(listener, EndpointListenerNotifier.filtersFromEEL(reference));
-            super.modifiedService(reference, listener);
-        }
-
-        @Override
-        public void removedService(ServiceReference<EndpointEventListener> reference,
-                EndpointEventListener listener) {
-            notifier.remove(listener);
-            super.removedService(reference, listener);
-        }
-    }
-
     private final class RSATracker extends ServiceTracker<RemoteServiceAdmin, RemoteServiceAdmin> {
         private RSATracker(BundleContext context, Class<RemoteServiceAdmin> clazz,
                            ServiceTrackerCustomizer<RemoteServiceAdmin, RemoteServiceAdmin> customizer) {

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/1ad152b4/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/EndpointEventListenerTracker.java
----------------------------------------------------------------------
diff --git a/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/EndpointEventListenerTracker.java b/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/EndpointEventListenerTracker.java
new file mode 100644
index 0000000..1f8ba43
--- /dev/null
+++ b/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/EndpointEventListenerTracker.java
@@ -0,0 +1,39 @@
+package org.apache.aries.rsa.topologymanager;
+
+import org.apache.aries.rsa.topologymanager.exporter.EndpointListenerNotifier;
+import org.apache.aries.rsa.topologymanager.exporter.TopologyManagerExport;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.EndpointEventListener;
+import org.osgi.util.tracker.ServiceTracker;
+
+final class EndpointEventListenerTracker extends ServiceTracker<EndpointEventListener, EndpointEventListener> {
+    private TopologyManagerExport tmExport;
+
+    EndpointEventListenerTracker(BundleContext context, TopologyManagerExport tmExport) {
+        super(context, EndpointEventListener.class, null);
+        this.tmExport = tmExport;
+    }
+
+    @Override
+    public EndpointEventListener addingService(ServiceReference<EndpointEventListener> reference) {
+        EndpointEventListener listener = super.addingService(reference);
+        this.tmExport.addEPListener(listener, EndpointListenerNotifier.filtersFromEEL(reference));
+        return listener;
+    }
+
+    @Override
+    public void modifiedService(ServiceReference<EndpointEventListener> reference,
+            EndpointEventListener listener) {
+        this.tmExport.addEPListener(listener, EndpointListenerNotifier.filtersFromEEL(reference));
+        super.modifiedService(reference, listener);
+    }
+
+    @Override
+    public void removedService(ServiceReference<EndpointEventListener> reference,
+            EndpointEventListener listener) {
+        this.tmExport.removeEPListener(listener);
+        super.removedService(reference, listener);
+    }
+    
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/1ad152b4/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/EndpointListenerTracker.java
----------------------------------------------------------------------
diff --git a/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/EndpointListenerTracker.java b/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/EndpointListenerTracker.java
new file mode 100644
index 0000000..87ab0ae
--- /dev/null
+++ b/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/EndpointListenerTracker.java
@@ -0,0 +1,43 @@
+package org.apache.aries.rsa.topologymanager;
+
+import org.apache.aries.rsa.topologymanager.exporter.EndpointListenerAdapter;
+import org.apache.aries.rsa.topologymanager.exporter.EndpointListenerNotifier;
+import org.apache.aries.rsa.topologymanager.exporter.TopologyManagerExport;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.EndpointListener;
+import org.osgi.util.tracker.ServiceTracker;
+
+@SuppressWarnings("deprecation")
+final class EndpointListenerTracker extends ServiceTracker<EndpointListener, EndpointListener> {
+    private TopologyManagerExport tmExport;
+
+    EndpointListenerTracker(BundleContext context, TopologyManagerExport tmExport) {
+        super(context, EndpointListener.class, null);
+        this.tmExport = tmExport;
+    }
+
+    @Override
+    public EndpointListener addingService(ServiceReference<EndpointListener> reference) {
+        EndpointListener listener = super.addingService(reference);
+        EndpointListenerAdapter adapter = new EndpointListenerAdapter(listener);
+        tmExport.addEPListener(adapter, EndpointListenerNotifier.filtersFromEL(reference));
+        return listener;
+    }
+
+    @Override
+    public void modifiedService(ServiceReference<EndpointListener> reference,
+                                EndpointListener listener) {
+        super.modifiedService(reference, listener);
+        EndpointListenerAdapter adapter = new EndpointListenerAdapter(listener);
+        tmExport.addEPListener(adapter, EndpointListenerNotifier.filtersFromEL(reference));
+    }
+
+    @Override
+    public void removedService(ServiceReference<EndpointListener> reference,
+                               EndpointListener listener) {
+        EndpointListenerAdapter adapter = new EndpointListenerAdapter(listener);
+        tmExport.removeEPListener(adapter);
+        super.removedService(reference, listener);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/1ad152b4/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/exporter/EndpointListenerNotifier.java
----------------------------------------------------------------------
diff --git a/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/exporter/EndpointListenerNotifier.java b/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/exporter/EndpointListenerNotifier.java
index 827fe6a..b816306 100644
--- a/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/exporter/EndpointListenerNotifier.java
+++ b/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/exporter/EndpointListenerNotifier.java
@@ -18,6 +18,7 @@
  */
 package org.apache.aries.rsa.topologymanager.exporter;
 
+import java.util.Collection;
 import java.util.Dictionary;
 import java.util.HashSet;
 import java.util.Hashtable;
@@ -42,13 +43,11 @@ import org.slf4j.LoggerFactory;
  * Tracks EndpointListeners and allows to notify them of endpoints.
  */
 @SuppressWarnings("deprecation")
-public class EndpointListenerNotifier implements EndpointEventListener {
+public class EndpointListenerNotifier {
     private static final Logger LOG = LoggerFactory.getLogger(EndpointListenerNotifier.class);
     private Map<EndpointEventListener, Set<Filter>> listeners;
-    private EndpointRepository endpointRepo;
 
-    public EndpointListenerNotifier(final EndpointRepository endpointRepo) {
-        this.endpointRepo = endpointRepo;
+    public EndpointListenerNotifier() {
         this.listeners = new ConcurrentHashMap<EndpointEventListener, Set<Filter>>();
     }
    
@@ -73,32 +72,22 @@ public class EndpointListenerNotifier implements EndpointEventListener {
         }
         return filters;
     }
-
-    public void removre(EndpointListener ep) {
-        LOG.debug("EndpointListener modified");
-        EndpointListenerAdapter adapter = new EndpointListenerAdapter(ep);
-        listeners.remove(adapter);
-    }
     
-    public void add(EndpointEventListener ep, Set<Filter> filters) {
-        LOG.debug("new EndpointListener detected");
+    public void add(EndpointEventListener ep, Set<Filter> filters, Collection<EndpointDescription> endpoints) {
+        LOG.debug("EndpointListener added");
         listeners.put(ep, filters);
-        for (EndpointDescription endpoint : endpointRepo.getAllEndpoints()) {
+        for (EndpointDescription endpoint : endpoints) {
             EndpointEvent event = new EndpointEvent(EndpointEvent.ADDED, endpoint);
             notifyListener(event, ep, filters);
         }
     }
     
     public void remove(EndpointEventListener ep) {
-        LOG.debug("EndpointListener modified");
+        LOG.debug("EndpointListener removed");
         listeners.remove(ep);
     }
     
-    /**
-     * NOTICE: filter will be ignored
-     */
-    @Override
-    public void endpointChanged(EndpointEvent event, String filter) {
+    public void sendEvent(EndpointEvent event) {
         for (EndpointEventListener listener : listeners.keySet()) {
             Set<Filter> filters = listeners.get(listener);
             notifyListener(event, listener, filters);

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/1ad152b4/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/exporter/EndpointRepository.java
----------------------------------------------------------------------
diff --git a/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/exporter/EndpointRepository.java b/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/exporter/EndpointRepository.java
deleted file mode 100644
index b02cbba..0000000
--- a/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/exporter/EndpointRepository.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/**
- * 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.aries.rsa.topologymanager.exporter;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.osgi.framework.Bundle;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.remoteserviceadmin.EndpointDescription;
-import org.osgi.service.remoteserviceadmin.EndpointEvent;
-import org.osgi.service.remoteserviceadmin.EndpointEventListener;
-import org.osgi.service.remoteserviceadmin.RemoteServiceAdmin;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Holds all endpoints that are exported by a TopologyManager. For each ServiceReference that is exported a
- * map is maintained which contains information on the endpoints for each RemoteAdminService that created the
- * endpoints.
- */
-@SuppressWarnings("rawtypes")
-public class EndpointRepository {
-
-    private static final Logger LOG = LoggerFactory.getLogger(EndpointRepository.class);
-
-    private final Map<ServiceReference, Map<RemoteServiceAdmin, Collection<EndpointDescription>>> exportedServices
-        = new LinkedHashMap<ServiceReference, Map<RemoteServiceAdmin, Collection<EndpointDescription>>>();
-
-    private EndpointEventListener notifier;
-    
-    public void setNotifier(EndpointEventListener notifier) {
-        this.notifier = notifier;
-    }
-    
-    
-    /**
-     * Remove all services exported by the given rsa.
-     *
-     * @param rsa the RemoteServiceAdmin to remove
-     * @return list of removed endpoints
-     */
-    public synchronized List<EndpointDescription> removeRemoteServiceAdmin(RemoteServiceAdmin rsa) {
-        LOG.debug("RemoteServiceAdmin removed: {}", rsa.getClass().getName());
-        List<EndpointDescription> removedEndpoints = new ArrayList<EndpointDescription>();
-        for (Map<RemoteServiceAdmin, Collection<EndpointDescription>> exports : exportedServices.values()) {
-            Collection<EndpointDescription> endpoints = exports.get(rsa);
-            if (endpoints != null) {
-                removedEndpoints.addAll(endpoints);
-                exports.remove(rsa);
-            }
-        }
-        endpointsRemoved(removedEndpoints);
-        return removedEndpoints;
-    }
-
-    public synchronized void removeService(ServiceReference sref) {
-        List<EndpointDescription> removedEndpoints = new ArrayList<EndpointDescription>();
-        Map<RemoteServiceAdmin, Collection<EndpointDescription>> rsaToEndpoints = exportedServices.get(sref);
-        if (rsaToEndpoints != null) {
-            for (Collection<EndpointDescription> endpoints : rsaToEndpoints.values()) {
-                removedEndpoints.addAll(endpoints);
-            }
-            exportedServices.remove(sref);
-        }
-        endpointsRemoved(removedEndpoints);
-    }
-
-    public synchronized void addService(ServiceReference sref) {
-        if (!exportedServices.containsKey(sref)) {
-            Bundle bundle = sref.getBundle();
-            if (bundle != null) {
-                LOG.info("Marking service from bundle {} for export", bundle.getSymbolicName());
-            }
-            exportedServices.put(sref, new LinkedHashMap<RemoteServiceAdmin, Collection<EndpointDescription>>());
-        }
-    }
-
-    public synchronized void addEndpoints(ServiceReference sref, RemoteServiceAdmin rsa,
-                                   List<EndpointDescription> endpoints) {
-        addService(sref);
-        Map<RemoteServiceAdmin, Collection<EndpointDescription>> exports = exportedServices.get(sref);
-        exports.put(rsa, endpoints);
-        endpointsAdded(endpoints);
-    }
-
-    synchronized boolean isAlreadyExportedForRsa(ServiceReference sref, RemoteServiceAdmin rsa) {
-        Map<RemoteServiceAdmin, Collection<EndpointDescription>> exports = exportedServices.get(sref);
-        return exports != null && exports.containsKey(rsa);
-    }
-
-    public synchronized Collection<EndpointDescription> getAllEndpoints() {
-        List<EndpointDescription> allEndpoints = new ArrayList<EndpointDescription>();
-        for (Map<RemoteServiceAdmin, Collection<EndpointDescription>> exports : exportedServices.values()) {
-            for (Collection<EndpointDescription> endpoints : exports.values()) {
-                allEndpoints.addAll(endpoints);
-            }
-        }
-        return allEndpoints;
-    }
-
-    public synchronized Set<ServiceReference> getServicesToBeExportedFor(RemoteServiceAdmin rsa) {
-        Set<ServiceReference> servicesToBeExported = new HashSet<ServiceReference>();
-        for (Map.Entry<ServiceReference, Map<RemoteServiceAdmin, Collection<EndpointDescription>>> entry
-                : exportedServices.entrySet()) {
-            if (!entry.getValue().containsKey(rsa)) {
-                servicesToBeExported.add(entry.getKey());
-            }
-        }
-        return servicesToBeExported;
-    }
-
-    private void endpointsAdded(List<EndpointDescription> endpoints) {
-        for (EndpointDescription epd : endpoints) {
-            EndpointEvent event = new EndpointEvent(EndpointEvent.ADDED, epd);
-            notifier.endpointChanged(event, null);
-        }
-    }
-    
-    private void endpointsRemoved(List<EndpointDescription> endpoints) {
-        for (EndpointDescription epd : endpoints) {
-            EndpointEvent event = new EndpointEvent(EndpointEvent.REMOVED, epd);
-            notifier.endpointChanged(event, null);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/1ad152b4/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/exporter/ServiceExportsRepository.java
----------------------------------------------------------------------
diff --git a/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/exporter/ServiceExportsRepository.java b/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/exporter/ServiceExportsRepository.java
new file mode 100644
index 0000000..99a2faf
--- /dev/null
+++ b/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/exporter/ServiceExportsRepository.java
@@ -0,0 +1,122 @@
+/**
+ * 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.aries.rsa.topologymanager.exporter;
+
+import java.io.Closeable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.EndpointEvent;
+import org.osgi.service.remoteserviceadmin.ExportReference;
+import org.osgi.service.remoteserviceadmin.ExportRegistration;
+import org.osgi.service.remoteserviceadmin.RemoteServiceAdmin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Holds all Exports of a given RemoteServiceAdmin
+ */
+public class ServiceExportsRepository  implements Closeable {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ServiceExportsRepository.class);
+
+    private RemoteServiceAdmin rsa;
+    private EndpointListenerNotifier notifier;
+
+    private final Map<ServiceReference<?>, Collection<ExportRegistration>> exportsMap = new LinkedHashMap<>();
+
+
+    public ServiceExportsRepository(RemoteServiceAdmin rsa, EndpointListenerNotifier notifier) {
+        this.rsa = rsa;
+        this.notifier = notifier;
+    }
+    
+    public void close() {
+        LOG.debug("Closing registry for RemoteServiceAdmin {}", rsa.getClass().getName());
+        for (ServiceReference<?> sref : exportsMap.keySet()) {
+            removeService(sref);
+        }
+    }
+
+    private void closeReg(ExportRegistration reg) {
+        ExportReference exportReference = reg.getExportReference();
+        if (exportReference != null) {
+            EndpointDescription endpoint = exportReference.getExportedEndpoint();
+            notifier.sendEvent(new EndpointEvent(EndpointEvent.REMOVED, endpoint));
+            reg.close();
+        }
+    }
+    
+    public synchronized void addService(ServiceReference<?> sref, Collection<ExportRegistration> exports) {
+        exportsMap.put(sref, new ArrayList<ExportRegistration>(exports));
+        for (ExportRegistration reg : exports) {
+            ExportReference exportReference = reg.getExportReference();
+            if  (exportReference != null) {
+                EndpointDescription endpoint = exportReference.getExportedEndpoint();
+                EndpointEvent event = new EndpointEvent(EndpointEvent.ADDED, endpoint);
+                notifier.sendEvent(event);
+            }
+        }
+    }
+    
+
+    public synchronized void modifyService(ServiceReference<?> sref) {
+        Collection<ExportRegistration> exports = exportsMap.get(sref);
+        if (exports != null) {
+            for (ExportRegistration reg : exports) {
+                reg.update(null);
+                ExportReference exportReference = reg.getExportReference();
+                if  (exportReference != null) {
+                    EndpointDescription endpoint = exportReference.getExportedEndpoint();
+                    EndpointEvent event = new EndpointEvent(EndpointEvent.MODIFIED, endpoint);
+                    notifier.sendEvent(event);
+                }
+            }
+        }
+    }
+
+    public synchronized void removeService(ServiceReference<?> sref) {
+        Collection<ExportRegistration> exports = exportsMap.get(sref);
+        if (exports != null) {
+            for (ExportRegistration reg : exports) {
+                closeReg(reg);
+            }
+            exports.clear();
+        }
+    }
+    
+    public List<EndpointDescription> getAllEndpoints() {
+        List<EndpointDescription> endpoints = new ArrayList<>();
+        for (ServiceReference<?> sref : exportsMap.keySet()) {
+            Collection<ExportRegistration> exports = exportsMap.get(sref);
+            for (ExportRegistration reg : exports) {
+                ExportReference exportRef = reg.getExportReference();
+                if (exportRef != null) {
+                    endpoints.add(exportRef.getExportedEndpoint());
+                }
+            }
+        }
+        return endpoints;
+    }
+}

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/1ad152b4/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/exporter/TopologyManagerExport.java
----------------------------------------------------------------------
diff --git a/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/exporter/TopologyManagerExport.java b/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/exporter/TopologyManagerExport.java
index e11801c..257c924 100644
--- a/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/exporter/TopologyManagerExport.java
+++ b/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/exporter/TopologyManagerExport.java
@@ -20,6 +20,8 @@ package org.apache.aries.rsa.topologymanager.exporter;
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -29,11 +31,12 @@ import java.util.concurrent.Executor;
 import org.apache.aries.rsa.spi.ExportPolicy;
 import org.apache.aries.rsa.util.StringPlus;
 import org.osgi.framework.Bundle;
+import org.osgi.framework.Filter;
 import org.osgi.framework.ServiceEvent;
 import org.osgi.framework.ServiceListener;
 import org.osgi.framework.ServiceReference;
 import org.osgi.service.remoteserviceadmin.EndpointDescription;
-import org.osgi.service.remoteserviceadmin.ExportReference;
+import org.osgi.service.remoteserviceadmin.EndpointEventListener;
 import org.osgi.service.remoteserviceadmin.ExportRegistration;
 import org.osgi.service.remoteserviceadmin.RemoteConstants;
 import org.osgi.service.remoteserviceadmin.RemoteServiceAdmin;
@@ -56,44 +59,90 @@ public class TopologyManagerExport implements ServiceListener {
     private static final Logger LOG = LoggerFactory.getLogger(TopologyManagerExport.class);
 
     private final Executor execService;
-    private final EndpointRepository endpointRepo;
+    private final Map<RemoteServiceAdmin, ServiceExportsRepository> endpointRepo;
     private ExportPolicy policy;
-    private final Set<RemoteServiceAdmin> rsaSet;
-
-
-    public TopologyManagerExport(final EndpointRepository endpointRepo, Executor executor, ExportPolicy policy) {
-        this.endpointRepo = endpointRepo;
-        this.policy = policy;
-        this.rsaSet = new HashSet<RemoteServiceAdmin>();
+    private Map<Integer, String> typeNames;
+    private final EndpointListenerNotifier notifier;
+    private Set<ServiceReference<?>> toBeExported;
+
+    public TopologyManagerExport(
+            EndpointListenerNotifier notifier,
+            Executor executor, 
+            ExportPolicy policy) {
+        this.notifier = notifier;
         this.execService = executor;
+        this.policy = policy;
+        this.endpointRepo = new HashMap<>();
+        this.toBeExported = new HashSet<>();
+        createTypeNames();
+    }
+
+    private void createTypeNames() {
+        this.typeNames = new HashMap<>();
+        this.typeNames.put(ServiceEvent.MODIFIED, "modified");
+        this.typeNames.put(ServiceEvent.MODIFIED_ENDMATCH, "modified endmatch");
+        this.typeNames.put(ServiceEvent.REGISTERED, "registered");
+        this.typeNames.put(ServiceEvent.UNREGISTERING, "unregistering");
     }
 
     // track all service registrations so we can export any services that are configured to be exported
     // ServiceListener events may be delivered out of order, concurrently, re-entrant, etc. (see spec or docs)
     public void serviceChanged(ServiceEvent event) {
+        LOG.info("Received ServiceEvent type: {}, sref: {}", getTypeName(event), event.getServiceReference());
         ServiceReference<?> sref = event.getServiceReference();
-        if (event.getType() == ServiceEvent.REGISTERED) {
-            LOG.debug("Received REGISTERED ServiceEvent: {}", event);
-            export(sref);
-        } else if (event.getType() == ServiceEvent.UNREGISTERING) {
-            LOG.debug("Received UNREGISTERING ServiceEvent: {}", event);
-            endpointRepo.removeService(sref);
+        switch (event.getType()) {
+        case ServiceEvent.REGISTERED:
+            exportInBackground(sref);
+            break;
+
+        case ServiceEvent.MODIFIED:
+            modified(sref);
+            break;
+            
+        case ServiceEvent.MODIFIED_ENDMATCH:
+            remove(sref);
+            break;
+
+        case ServiceEvent.UNREGISTERING:
+            remove(sref);
+            break;
         }
     }
 
+    private void modified(ServiceReference<?> sref) {
+        for (RemoteServiceAdmin rsa : endpointRepo.keySet()) {
+            ServiceExportsRepository repo = endpointRepo.get(rsa);
+            repo.modifyService(sref);
+        }
+    }
+
+    private void remove(ServiceReference<?> sref) {
+        toBeExported.remove(sref);
+        for (RemoteServiceAdmin rsa : endpointRepo.keySet()) {
+            ServiceExportsRepository repo = endpointRepo.get(rsa);
+            repo.removeService(sref);
+        }
+    }
+    
+    public String getTypeName(ServiceEvent event) {
+        return typeNames.get(event.getType());
+    }
+
     public void add(RemoteServiceAdmin rsa) {
-        rsaSet.add(rsa);
-        for (ServiceReference<?> serviceRef : endpointRepo.getServicesToBeExportedFor(rsa)) {
-            export(serviceRef);
+        endpointRepo.put(rsa,  new ServiceExportsRepository(rsa, notifier));
+        for (ServiceReference<?> serviceRef : toBeExported) {
+            exportInBackground(serviceRef);
         }
     };
 
     public void remove(RemoteServiceAdmin rsa) {
-        rsaSet.remove(rsa);
-        endpointRepo.removeRemoteServiceAdmin(rsa);
+        ServiceExportsRepository repo = endpointRepo.remove(rsa);
+        if (repo != null) {
+            repo.close();
+        }
     };
 
-    private void export(final ServiceReference<?> sref) {
+    private void exportInBackground(final ServiceReference<?> sref) {
         execService.execute(new Runnable() {
             public void run() {
                 doExport(sref);
@@ -108,92 +157,82 @@ public class TopologyManagerExport implements ServiceListener {
             return;
         }
         LOG.debug("Exporting service {}", sref);
-        endpointRepo.addService(sref); // mark for future export even if there are currently no RSAs
-        if (rsaSet.size() == 0) {
-            LOG.error("No RemoteServiceAdmin available! Unable to export service from bundle {}, interfaces: {}",
+        toBeExported.add(sref);
+        if (endpointRepo.size() == 0) {
+            LOG.error("Unable to export service from bundle {}, interfaces: {} as no RemoteServiceAdmin is available. Marked for later export.",
                     getSymbolicName(sref.getBundle()),
                     sref.getProperty(org.osgi.framework.Constants.OBJECTCLASS));
             return;
         }
 
-        HashSet<RemoteServiceAdmin> rsaSetCopy = new HashSet<>(rsaSet);
-        for (RemoteServiceAdmin remoteServiceAdmin : rsaSetCopy) {
-            LOG.debug("TopologyManager: handling remoteServiceAdmin " + remoteServiceAdmin);
-            if (endpointRepo.isAlreadyExportedForRsa(sref, remoteServiceAdmin)) {
-                LOG.debug("already handled by this remoteServiceAdmin -> skipping");
-            } else {
-                exportServiceUsingRemoteServiceAdmin(sref, remoteServiceAdmin, addProps);
-            }
+        for (RemoteServiceAdmin remoteServiceAdmin : endpointRepo.keySet()) {
+            ServiceExportsRepository repo = endpointRepo.get(remoteServiceAdmin);
+            Collection<ExportRegistration> regs = exportService(remoteServiceAdmin, sref, addProps);
+            repo.addService(sref, regs);
         }
     }
-
-    private boolean shouldExport(ServiceReference<?> sref, Map<String, ?> addProps) {
-        List<String> exported= StringPlus.normalize(sref.getProperty(RemoteConstants.SERVICE_EXPORTED_INTERFACES));
-        List<String> addExported = StringPlus.normalize(addProps.get(RemoteConstants.SERVICE_EXPORTED_INTERFACES));
-        return sizeOf(exported) + sizeOf(addExported) > 0;
-    }
-
-    private int sizeOf(List<String> list) {
-        return list == null ? 0 : list.size();
-    }
-
-    private Object getSymbolicName(Bundle bundle) {
-        return bundle == null ? null : bundle.getSymbolicName();
-    }
-
-    private void exportServiceUsingRemoteServiceAdmin(final ServiceReference<?> sref,
-                                                      final RemoteServiceAdmin remoteServiceAdmin,
-                                                      Map<String, ?> addProps) {
+    
+    private static Collection<ExportRegistration> exportService(
+            final RemoteServiceAdmin rsa,
+            final ServiceReference<?> sref, 
+            final Map<String, ?> addProps) {
         // abort if the service was unregistered by the time we got here
         // (we check again at the end, but this optimization saves unnecessary heavy processing)
         if (sref.getBundle() == null) {
             LOG.info("TopologyManager: export aborted for {} since it was unregistered", sref);
-            endpointRepo.removeService(sref);
-            return;
+            return Collections.emptyList();
         }
 
-        LOG.debug("exporting {}...", sref);
-        // TODO: additional parameter Map?
-        Collection<ExportRegistration> exportRegs = remoteServiceAdmin.exportService(sref, addProps);
+        LOG.debug("exporting Service {} using RemoteServiceAdmin {}", sref, rsa.getClass().getName());
+        Collection<ExportRegistration> exportRegs = rsa.exportService(sref, addProps);
+
         // process successful/failed registrations
-        List<EndpointDescription> endpoints = new ArrayList<EndpointDescription>();
         for (ExportRegistration reg : exportRegs) {
             if (reg.getException() == null) {
-                EndpointDescription endpoint = getExportedEndpoint(reg);
-                LOG.debug("TopologyManager: export succeeded for {}, endpoint {}, rsa {}", sref, endpoint, remoteServiceAdmin.getClass());
-                endpoints.add(endpoint);
+                EndpointDescription endpoint = reg.getExportReference().getExportedEndpoint();
+                LOG.info("TopologyManager: export succeeded for {}, endpoint {}, rsa {}", sref, endpoint, rsa.getClass().getName());
             } else {
                 LOG.error("TopologyManager: export failed for {}", sref, reg.getException());
                 reg.close();
             }
         }
+
         // abort export if service was unregistered in the meanwhile (since we have a race
         // with the unregister event which may have already been handled, so we'll miss it)
         if (sref.getBundle() == null) {
             LOG.info("TopologyManager: export reverted for {} since service was unregistered", sref);
-            endpointRepo.removeService(sref);
             for (ExportRegistration reg : exportRegs) {
                 reg.close();
             }
-            return;
-        }
-        // add the new exported endpoints
-        if (!endpoints.isEmpty()) {
-            LOG.info("TopologyManager: export successful for {}, endpoints: {}", sref, endpoints);
-            endpointRepo.addEndpoints(sref, remoteServiceAdmin, endpoints);
         }
+        
+        return exportRegs;
     }
 
-    /**
-     * Retrieves an exported Endpoint (while safely handling nulls).
-     *
-     * @param exReg an export registration
-     * @return exported Endpoint or null if not present
-     */
-    private EndpointDescription getExportedEndpoint(ExportRegistration exReg) {
-        ExportReference ref = (exReg == null) ? null : exReg.getExportReference();
-        return (ref == null) ? null : ref.getExportedEndpoint();
+    private boolean shouldExport(ServiceReference<?> sref, Map<String, ?> addProps) {
+        List<String> exported= StringPlus.normalize(sref.getProperty(RemoteConstants.SERVICE_EXPORTED_INTERFACES));
+        List<String> addExported = StringPlus.normalize(addProps.get(RemoteConstants.SERVICE_EXPORTED_INTERFACES));
+        return sizeOf(exported) + sizeOf(addExported) > 0;
+    }
+
+    private int sizeOf(List<String> list) {
+        return list == null ? 0 : list.size();
+    }
+
+    private Object getSymbolicName(Bundle bundle) {
+        return bundle == null ? null : bundle.getSymbolicName();
     }
 
+    public void addEPListener(EndpointEventListener epListener, Set<Filter> filters) {
+        Collection<EndpointDescription> endpoints = new ArrayList<>();
+        for (RemoteServiceAdmin rsa : endpointRepo.keySet()) {
+            ServiceExportsRepository repo = endpointRepo.get(rsa);
+            endpoints.addAll(repo.getAllEndpoints());
+        }
+        notifier.add(epListener, filters, endpoints);
+    }
 
+    public void removeEPListener(EndpointEventListener listener) {
+        notifier.remove(listener);
+    }
 }

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/1ad152b4/topology-manager/src/test/java/org/apache/aries/rsa/topologymanager/exporter/EndpointListenerNotifierTest.java
----------------------------------------------------------------------
diff --git a/topology-manager/src/test/java/org/apache/aries/rsa/topologymanager/exporter/EndpointListenerNotifierTest.java b/topology-manager/src/test/java/org/apache/aries/rsa/topologymanager/exporter/EndpointListenerNotifierTest.java
index 3b88abd..bc3c084 100644
--- a/topology-manager/src/test/java/org/apache/aries/rsa/topologymanager/exporter/EndpointListenerNotifierTest.java
+++ b/topology-manager/src/test/java/org/apache/aries/rsa/topologymanager/exporter/EndpointListenerNotifierTest.java
@@ -22,17 +22,18 @@ import static java.util.Arrays.asList;
 import static org.easymock.EasyMock.capture;
 import static org.easymock.EasyMock.expectLastCall;
 import static org.easymock.EasyMock.newCapture;
-import static org.hamcrest.Matchers.samePropertyValuesAs;
 import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
 
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Dictionary;
 import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -52,6 +53,9 @@ import org.osgi.service.remoteserviceadmin.EndpointEvent;
 import org.osgi.service.remoteserviceadmin.EndpointEventListener;
 import org.osgi.service.remoteserviceadmin.RemoteConstants;
 
+import com.shazam.shazamcrest.MatcherAssert;
+import com.shazam.shazamcrest.matcher.Matchers;
+
 @SuppressWarnings({
     "rawtypes", "unchecked",
    })
@@ -73,21 +77,24 @@ public class EndpointListenerNotifierTest {
         EndpointDescription endpoint1 = createEndpoint("myClass");
         EndpointDescription endpoint2 = createEndpoint("notMyClass");
 
-        EndpointRepository exportRepository = new EndpointRepository();
-        EndpointListenerNotifier notifier = new EndpointListenerNotifier(exportRepository);
-
         c.replay();
+        
+        EndpointListenerNotifier notifier = new EndpointListenerNotifier();
         Filter filter = FrameworkUtil.createFilter("(objectClass=myClass)");
-        notifier.add(epl, new HashSet(asList(filter)));
-        notifier.endpointChanged(new EndpointEvent(EndpointEvent.ADDED, endpoint1), null);
-        notifier.endpointChanged(new EndpointEvent(EndpointEvent.ADDED, endpoint2), null);
-        notifier.endpointChanged(new EndpointEvent(EndpointEvent.REMOVED, endpoint1), null);
-        notifier.endpointChanged(new EndpointEvent(EndpointEvent.REMOVED, endpoint2), null);
+        notifier.add(epl, new HashSet(asList(filter)), Collections.<EndpointDescription>emptyList());
+        notifier.sendEvent(new EndpointEvent(EndpointEvent.ADDED, endpoint1));
+        notifier.sendEvent(new EndpointEvent(EndpointEvent.ADDED, endpoint2));
+        notifier.sendEvent(new EndpointEvent(EndpointEvent.REMOVED, endpoint1));
+        notifier.sendEvent(new EndpointEvent(EndpointEvent.REMOVED, endpoint2));
         c.verify();
 
         // Expect listener to be called for endpoint1 but not for endpoint2 
-        assertThat(capturedEvents.getValues().get(0), samePropertyValuesAs(new EndpointEvent(EndpointEvent.ADDED, endpoint1)));
-        assertThat(capturedEvents.getValues().get(1), samePropertyValuesAs(new EndpointEvent(EndpointEvent.REMOVED, endpoint1)));
+        List<EndpointEvent> expected = Arrays.asList(
+                new EndpointEvent(EndpointEvent.ADDED, endpoint1),
+                new EndpointEvent(EndpointEvent.REMOVED, endpoint1)
+                );
+                
+        MatcherAssert.assertThat(capturedEvents.getValues(), Matchers.sameBeanAs(expected));
     }
 
     private EndpointDescription createEndpoint(String iface) {

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/1ad152b4/topology-manager/src/test/java/org/apache/aries/rsa/topologymanager/exporter/EndpointRepositoryTest.java
----------------------------------------------------------------------
diff --git a/topology-manager/src/test/java/org/apache/aries/rsa/topologymanager/exporter/EndpointRepositoryTest.java b/topology-manager/src/test/java/org/apache/aries/rsa/topologymanager/exporter/EndpointRepositoryTest.java
deleted file mode 100644
index 1b4941c..0000000
--- a/topology-manager/src/test/java/org/apache/aries/rsa/topologymanager/exporter/EndpointRepositoryTest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/**
- * 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.aries.rsa.topologymanager.exporter;
-
-import java.util.Arrays;
-import java.util.Hashtable;
-import java.util.Map;
-
-import org.easymock.EasyMock;
-import org.easymock.IMocksControl;
-import org.junit.Test;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.remoteserviceadmin.EndpointDescription;
-import org.osgi.service.remoteserviceadmin.EndpointEvent;
-import org.osgi.service.remoteserviceadmin.RemoteConstants;
-import org.osgi.service.remoteserviceadmin.RemoteServiceAdmin;
-
-public class EndpointRepositoryTest {
-
-    @Test
-    public void testAddRemove() throws InvalidSyntaxException {
-        EndpointDescription ep1 = createEndpoint("my");
-        
-        IMocksControl c = EasyMock.createControl();
-        ServiceReference<?> sref = createService(c);
-        RemoteServiceAdmin rsa = c.createMock(RemoteServiceAdmin.class);
-        RecordingEndpointEventListener notifier = new RecordingEndpointEventListener();
-        
-        c.replay();
-        EndpointRepository repo = new EndpointRepository();
-        repo.setNotifier(notifier);
-        repo.addEndpoints(sref, rsa, Arrays.asList(ep1));
-        repo.removeRemoteServiceAdmin(rsa);
-        c.verify();
-
-        notifier.matches(new EndpointEvent(EndpointEvent.ADDED, ep1), new EndpointEvent(EndpointEvent.REMOVED, ep1));
-    }
-    
-    private ServiceReference<?> createService(IMocksControl c) {
-        ServiceReference<?> sref = c.createMock(ServiceReference.class);
-        Bundle bundle = c.createMock(Bundle.class);
-        EasyMock.expect(bundle.getSymbolicName()).andReturn("myBundle");
-        EasyMock.expect(sref.getBundle()).andReturn(bundle);
-        return sref;
-    }
-
-    public EndpointDescription createEndpoint(String iface) {
-        Map<String, Object> props = new Hashtable<String, Object>(); 
-        props.put("objectClass", new String[]{iface});
-        props.put(RemoteConstants.ENDPOINT_ID, iface);
-        props.put(RemoteConstants.SERVICE_IMPORTED_CONFIGS, "any");
-        return new EndpointDescription(props);
-    }
-    
-    
-}

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/1ad152b4/topology-manager/src/test/java/org/apache/aries/rsa/topologymanager/exporter/TopologyManagerExportTest.java
----------------------------------------------------------------------
diff --git a/topology-manager/src/test/java/org/apache/aries/rsa/topologymanager/exporter/TopologyManagerExportTest.java b/topology-manager/src/test/java/org/apache/aries/rsa/topologymanager/exporter/TopologyManagerExportTest.java
index af070d6..c74f606 100644
--- a/topology-manager/src/test/java/org/apache/aries/rsa/topologymanager/exporter/TopologyManagerExportTest.java
+++ b/topology-manager/src/test/java/org/apache/aries/rsa/topologymanager/exporter/TopologyManagerExportTest.java
@@ -18,6 +18,10 @@
  */
 package org.apache.aries.rsa.topologymanager.exporter;
 
+import static com.shazam.shazamcrest.matcher.Matchers.sameBeanAs;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
@@ -26,6 +30,8 @@ import java.util.Map;
 import java.util.concurrent.Executor;
 
 import org.apache.aries.rsa.spi.ExportPolicy;
+import org.easymock.Capture;
+import org.easymock.CaptureType;
 import org.easymock.EasyMock;
 import org.easymock.IMocksControl;
 import org.junit.Before;
@@ -41,27 +47,30 @@ import org.osgi.service.remoteserviceadmin.ExportRegistration;
 import org.osgi.service.remoteserviceadmin.RemoteConstants;
 import org.osgi.service.remoteserviceadmin.RemoteServiceAdmin;
 
+import com.shazam.shazamcrest.MatcherAssert;
+
 @SuppressWarnings({"rawtypes", "unchecked"})
 public class TopologyManagerExportTest {
     
     private IMocksControl c;
     private RemoteServiceAdmin rsa;
-    private RecordingEndpointEventListener notifier;
-    private EndpointDescription epd;
-    private EndpointRepository endpointRepo;
+    private EndpointListenerNotifier notifier;
     private TopologyManagerExport exportManager;
+    private Capture<EndpointEvent> events;
 
     @Before
     public void start() {
         c = EasyMock.createControl();
         rsa = c.createMock(RemoteServiceAdmin.class);
-        notifier = new RecordingEndpointEventListener();
-        epd = createEndpoint();
-        endpointRepo = new EndpointRepository();
-        endpointRepo.setNotifier(notifier);
+
+        notifier = c.createMock(EndpointListenerNotifier.class);
+        events = EasyMock.newCapture(CaptureType.ALL);
+        notifier.sendEvent(EasyMock.capture(events));
+        EasyMock.expectLastCall().anyTimes();
+        
         Executor executor = syncExecutor();
         ExportPolicy policy = new DefaultExportPolicy();
-        exportManager = new TopologyManagerExport(endpointRepo, executor, policy);
+        exportManager = new TopologyManagerExport(notifier, executor, policy);
     }
 
     /**
@@ -72,42 +81,35 @@ public class TopologyManagerExportTest {
      */
     @Test
     public void testServiceExportUnexport() throws Exception {
-        ServiceReference sref = createUserService(c);
+        EndpointDescription epd = createEndpoint();
+        ServiceReference sref = createUserService("*");
         expectServiceExported(sref, epd);
+        EndpointDescription epd2 = createEndpoint();
+        ServiceReference sref2 = createUserService("*");
+        expectServiceExported(sref2, epd2);
 
         c.replay();
         exportManager.add(rsa);
         exportManager.serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED, sref));
-        exportManager.serviceChanged(new ServiceEvent(ServiceEvent.UNREGISTERING, sref));
+        exportManager.serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED, sref2));
         exportManager.serviceChanged(new ServiceEvent(ServiceEvent.MODIFIED, sref));
+        //exportManager.serviceChanged(new ServiceEvent(ServiceEvent.UNREGISTERING, sref));
         exportManager.remove(rsa);
         c.verify();
-        notifier.matches(
-                new EndpointEvent(EndpointEvent.ADDED, epd),
-                new EndpointEvent(EndpointEvent.REMOVED, epd)
-                );
 
-    }
-
-    @Test
-    public void testExportExisting() throws Exception {
-        ServiceReference sref = createUserService(c);
-        expectServiceExported(sref, epd);
-        
-        c.replay();
-        EndpointRepository endpointRepo = new EndpointRepository();
-        endpointRepo.setNotifier(notifier);
-        ExportPolicy policy = new DefaultExportPolicy();
-        TopologyManagerExport exportManager = new TopologyManagerExport(endpointRepo, syncExecutor(), policy);
-        exportManager.serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED, sref));
-        exportManager.add(rsa);
-        c.verify();
+        List<EndpointEvent> expectedEvents = Arrays.asList(
+                new EndpointEvent(EndpointEvent.ADDED, epd),
+                new EndpointEvent(EndpointEvent.ADDED, epd2),
+                new EndpointEvent(EndpointEvent.MODIFIED, epd),
+                new EndpointEvent(EndpointEvent.REMOVED, epd),
+                new EndpointEvent(EndpointEvent.REMOVED, epd2));
+        MatcherAssert.assertThat(events.getValues(), sameBeanAs(expectedEvents));
     }
 
     @Test
     public void testExportExistingMultipleInterfaces() throws Exception {
         List<String> exportedInterfaces = Arrays.asList("a.b.C","foo.Bar");
-        final ServiceReference sref = createUserService(c, exportedInterfaces);
+        final ServiceReference sref = createUserService(exportedInterfaces);
         expectServiceExported(sref, createEndpoint());
 
         c.replay();
@@ -119,11 +121,11 @@ public class TopologyManagerExportTest {
     @Test
     public void testExportExistingNoExportedInterfaces() throws Exception {
         String exportedInterfaces = "";
-        final ServiceReference sref = createUserService(c, exportedInterfaces);
+        final ServiceReference sref = createUserService(exportedInterfaces);
         c.replay();
 
         ExportPolicy policy = new DefaultExportPolicy();
-        TopologyManagerExport exportManager = new TopologyManagerExport(endpointRepo, syncExecutor(), policy);
+        TopologyManagerExport exportManager = new TopologyManagerExport(notifier, syncExecutor(), policy);
         exportManager.serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED, sref));
         exportManager.add(rsa);
         c.verify();
@@ -133,7 +135,7 @@ public class TopologyManagerExportTest {
             final ServiceReference sref,
             EndpointDescription epd) {
         ExportRegistration exportRegistration = createExportRegistration(c, epd);
-        EasyMock.expect(rsa.exportService(EasyMock.same(sref), (Map<String, Object>)EasyMock.anyObject()))
+        expect(rsa.exportService(EasyMock.same(sref), (Map<String, Object>)EasyMock.anyObject()))
             .andReturn(Collections.singletonList(exportRegistration)).once();
     }
 
@@ -149,9 +151,12 @@ public class TopologyManagerExportTest {
     private ExportRegistration createExportRegistration(IMocksControl c, EndpointDescription endpoint) {
         ExportRegistration exportRegistration = c.createMock(ExportRegistration.class);
         ExportReference exportReference = c.createMock(ExportReference.class);
-        EasyMock.expect(exportRegistration.getExportReference()).andReturn(exportReference).anyTimes();
-        EasyMock.expect(exportRegistration.getException()).andReturn(null).anyTimes();
-        EasyMock.expect(exportReference.getExportedEndpoint()).andReturn(endpoint).anyTimes();
+        expect(exportRegistration.getExportReference()).andReturn(exportReference).anyTimes();
+        expect(exportRegistration.getException()).andReturn(null).anyTimes();
+        expect(exportReference.getExportedEndpoint()).andReturn(endpoint).anyTimes();
+        exportRegistration.close();
+        expectLastCall().anyTimes();
+        expect(exportRegistration.update(EasyMock.isNull(Map.class))).andReturn(endpoint).anyTimes();
         return exportRegistration;
     }
 
@@ -163,20 +168,14 @@ public class TopologyManagerExportTest {
         return new EndpointDescription(props);
     }
 
-    private ServiceReference createUserService(IMocksControl c) {
-        return createUserService(c, "*");
-    }
-
-    private ServiceReference createUserService(IMocksControl c, Object exportedInterfaces) {
+    private ServiceReference createUserService(Object exportedInterfaces) {
         final ServiceReference sref = c.createMock(ServiceReference.class);
-        EasyMock.expect(sref.getProperty(EasyMock.same(RemoteConstants.SERVICE_EXPORTED_INTERFACES)))
+        expect(sref.getProperty(EasyMock.same(RemoteConstants.SERVICE_EXPORTED_INTERFACES)))
             .andReturn(exportedInterfaces).anyTimes();
         Bundle srefBundle = c.createMock(Bundle.class);
-        if(!"".equals(exportedInterfaces)) {
-            EasyMock.expect(sref.getBundle()).andReturn(srefBundle).atLeastOnce();
-            EasyMock.expect(srefBundle.getSymbolicName()).andReturn("serviceBundleName").atLeastOnce();
-        }
-        EasyMock.expect(sref.getProperty("objectClass")).andReturn("org.My").anyTimes();
+        expect(sref.getBundle()).andReturn(srefBundle).anyTimes();
+        expect(srefBundle.getSymbolicName()).andReturn("serviceBundleName").anyTimes();
+        expect(sref.getProperty("objectClass")).andReturn("org.My").anyTimes();
         return sref;
     }
 }


[3/3] aries-rsa git commit: [ARIES-1763] Correctly close exports when service goes away

Posted by cs...@apache.org.
[ARIES-1763] Correctly close exports when service goes away


Project: http://git-wip-us.apache.org/repos/asf/aries-rsa/repo
Commit: http://git-wip-us.apache.org/repos/asf/aries-rsa/commit/55e2c1cb
Tree: http://git-wip-us.apache.org/repos/asf/aries-rsa/tree/55e2c1cb
Diff: http://git-wip-us.apache.org/repos/asf/aries-rsa/diff/55e2c1cb

Branch: refs/heads/master
Commit: 55e2c1cb4767d5155a171e349514c91a7ac466f3
Parents: a5c8d7d
Author: Christian Schneider <cs...@adobe.com>
Authored: Mon Feb 5 10:44:54 2018 +0100
Committer: Christian Schneider <cs...@adobe.com>
Committed: Mon Feb 5 10:44:54 2018 +0100

----------------------------------------------------------------------
 .../aries/rsa/core/ExportRegistrationImpl.java  | 18 ++++++-
 .../aries/rsa/core/RemoteServiceAdminCore.java  | 23 +++++++--
 .../core/DistributionProviderTrackerTest.java   | 34 +++++++-----
 .../rsa/core/RemoteServiceAdminCoreTest.java    | 54 ++++++++------------
 .../exporter/TopologyManagerExport.java         | 24 ++++-----
 5 files changed, 91 insertions(+), 62 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/55e2c1cb/rsa/src/main/java/org/apache/aries/rsa/core/ExportRegistrationImpl.java
----------------------------------------------------------------------
diff --git a/rsa/src/main/java/org/apache/aries/rsa/core/ExportRegistrationImpl.java b/rsa/src/main/java/org/apache/aries/rsa/core/ExportRegistrationImpl.java
index 81bb859..a02ff3a 100644
--- a/rsa/src/main/java/org/apache/aries/rsa/core/ExportRegistrationImpl.java
+++ b/rsa/src/main/java/org/apache/aries/rsa/core/ExportRegistrationImpl.java
@@ -21,6 +21,7 @@ package org.apache.aries.rsa.core;
 import java.io.Closeable;
 import java.io.IOException;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
 
@@ -30,6 +31,7 @@ import org.osgi.framework.ServiceReference;
 import org.osgi.service.remoteserviceadmin.EndpointDescription;
 import org.osgi.service.remoteserviceadmin.ExportReference;
 import org.osgi.service.remoteserviceadmin.ExportRegistration;
+import org.osgi.service.remoteserviceadmin.RemoteConstants;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -110,6 +112,7 @@ public class ExportRegistrationImpl implements ExportRegistration {
     }
 
     public final void close() {
+        closeHandler.onClose(this);
         synchronized (this) {
             if (closed) {
                 return;
@@ -117,7 +120,6 @@ public class ExportRegistrationImpl implements ExportRegistration {
             closed = true;
         }
 
-        closeHandler.onClose(this);
         if (exportReference != null) {
             exportReference.close();
         }
@@ -181,9 +183,21 @@ public class ExportRegistrationImpl implements ExportRegistration {
             return null;
         }
         ServiceReference<?> sref = getExportReference().getExportedService();
-        EndpointDescription epd = new EndpointDescription(sref, properties);
+        
+        HashMap<String, Object> props = new HashMap<>(properties);
+        EndpointDescription oldEpd = getExportReference().getExportedEndpoint();
+        copyIfNull(props, oldEpd, RemoteConstants.ENDPOINT_ID);
+        copyIfNull(props, oldEpd, RemoteConstants.SERVICE_IMPORTED_CONFIGS);
+
+        EndpointDescription epd = new EndpointDescription(sref, props);
         exportReference = new ExportReferenceImpl(sref, epd);
         this.sender.notifyUpdate(this.getExportReference());
         return epd;
     }
+
+    private void copyIfNull(HashMap<String, Object> props, EndpointDescription oldEpd, String key) {
+        if (props.get(key) == null) {
+            props.put(key, oldEpd.getProperties().get(key));
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/55e2c1cb/rsa/src/main/java/org/apache/aries/rsa/core/RemoteServiceAdminCore.java
----------------------------------------------------------------------
diff --git a/rsa/src/main/java/org/apache/aries/rsa/core/RemoteServiceAdminCore.java b/rsa/src/main/java/org/apache/aries/rsa/core/RemoteServiceAdminCore.java
index 98fca14..f8df7c8 100644
--- a/rsa/src/main/java/org/apache/aries/rsa/core/RemoteServiceAdminCore.java
+++ b/rsa/src/main/java/org/apache/aries/rsa/core/RemoteServiceAdminCore.java
@@ -39,6 +39,7 @@ import org.apache.aries.rsa.util.EndpointHelper;
 import org.apache.aries.rsa.util.StringPlus;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceEvent;
 import org.osgi.framework.ServiceListener;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
@@ -84,13 +85,26 @@ public class RemoteServiceAdminCore implements RemoteServiceAdmin {
         this.packageUtil = packageUtil;
         this.closeHandler = new CloseHandler() {
             public void onClose(ExportRegistration exportReg) {
-                removeExportRegistration((ExportRegistrationImpl) exportReg);
+                removeExportRegistration(exportReg);
             }
 
             public void onClose(ImportRegistration importReg) {
                 removeImportRegistration((ImportRegistrationImpl) importReg);
             }
         };
+        createServiceListener();
+    }
+    
+    // listen for exported services being unregistered so we can close the export
+    protected void createServiceListener() {
+        this.exportedServiceListener = new ServiceListener() {
+            public void serviceChanged(ServiceEvent event) {
+                if (event.getType() == ServiceEvent.UNREGISTERING) {
+                    removeServiceExports(event.getServiceReference());
+                }
+            }
+        };
+        this.bctx.addServiceListener(exportedServiceListener);
     }
 
     @Override
@@ -216,6 +230,9 @@ public class RemoteServiceAdminCore implements RemoteServiceAdmin {
                 return null;
             }
             return new ExportRegistrationImpl(serviceReference, endpoint, closeHandler, eventProducer);
+        } catch (IllegalArgumentException e) {
+            // TCK expects this for garbage input
+            throw e;
         } catch (Exception e) {
             return new ExportRegistrationImpl(e, closeHandler, eventProducer);
         }
@@ -476,14 +493,14 @@ public class RemoteServiceAdminCore implements RemoteServiceAdmin {
      *
      * @param eri the export registration to remove
      */
-    protected void removeExportRegistration(ExportRegistrationImpl eri) {
+    protected void removeExportRegistration(ExportRegistration eri) {
         synchronized (exportedServices) {
             for (Iterator<Collection<ExportRegistration>> it = exportedServices.values().iterator(); it.hasNext();) {
                 Collection<ExportRegistration> value = it.next();
                 for (Iterator<ExportRegistration> it2 = value.iterator(); it2.hasNext();) {
                     ExportRegistration er = it2.next();
                     if (er.equals(eri)) {
-                        eventProducer.notifyRemoval(eri.getExportReferenceAlways());
+                        eventProducer.notifyRemoval(eri.getExportReference());
                         it2.remove();
                         if (value.isEmpty()) {
                             it.remove();

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/55e2c1cb/rsa/src/test/java/org/apache/aries/rsa/core/DistributionProviderTrackerTest.java
----------------------------------------------------------------------
diff --git a/rsa/src/test/java/org/apache/aries/rsa/core/DistributionProviderTrackerTest.java b/rsa/src/test/java/org/apache/aries/rsa/core/DistributionProviderTrackerTest.java
index 1468b8b..a18dae5 100644
--- a/rsa/src/test/java/org/apache/aries/rsa/core/DistributionProviderTrackerTest.java
+++ b/rsa/src/test/java/org/apache/aries/rsa/core/DistributionProviderTrackerTest.java
@@ -18,6 +18,11 @@
  */
 package org.apache.aries.rsa.core;
 
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.anyString;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+
 import java.util.Dictionary;
 
 import org.apache.aries.rsa.spi.DistributionProvider;
@@ -29,6 +34,7 @@ import org.osgi.framework.Filter;
 import org.osgi.framework.FrameworkUtil;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceListener;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.remoteserviceadmin.RemoteConstants;
@@ -45,19 +51,21 @@ public class DistributionProviderTrackerTest {
         DistributionProvider provider = c.createMock(DistributionProvider.class);
         
         ServiceReference<DistributionProvider> providerRef = c.createMock(ServiceReference.class);
-        EasyMock.expect(providerRef.getProperty(RemoteConstants.REMOTE_INTENTS_SUPPORTED)).andReturn("");
-        EasyMock.expect(providerRef.getProperty(RemoteConstants.REMOTE_CONFIGS_SUPPORTED)).andReturn("");
+        expect(providerRef.getProperty(RemoteConstants.REMOTE_INTENTS_SUPPORTED)).andReturn("");
+        expect(providerRef.getProperty(RemoteConstants.REMOTE_CONFIGS_SUPPORTED)).andReturn("");
 
         BundleContext context = c.createMock(BundleContext.class);
         String filterSt = String.format("(objectClass=%s)", DistributionProvider.class.getName());
         Filter filter = FrameworkUtil.createFilter(filterSt);
-        EasyMock.expect(context.createFilter(filterSt)).andReturn(filter);
-        EasyMock.expect(context.getService(providerRef)).andReturn(provider);
+        expect(context.createFilter(filterSt)).andReturn(filter);
+        expect(context.getService(providerRef)).andReturn(provider);
         ServiceRegistration rsaReg = c.createMock(ServiceRegistration.class);
-        EasyMock.expect(context.registerService(EasyMock.isA(String.class), EasyMock.isA(ServiceFactory.class), 
+        expect(context.registerService(EasyMock.isA(String.class), EasyMock.isA(ServiceFactory.class), 
                                                 EasyMock.isA(Dictionary.class)))
             .andReturn(rsaReg).atLeastOnce();
-
+        context.addServiceListener(anyObject(ServiceListener.class));
+        expectLastCall().anyTimes();
+        
         final BundleContext apiContext = c.createMock(BundleContext.class);
         c.replay();
         DistributionProviderTracker tracker = new DistributionProviderTracker(context) {
@@ -83,19 +91,21 @@ public class DistributionProviderTrackerTest {
         DistributionProvider provider = c.createMock(DistributionProvider.class);
 
         ServiceReference<DistributionProvider> providerRef = c.createMock(ServiceReference.class);
-        EasyMock.expect(providerRef.getProperty(RemoteConstants.REMOTE_INTENTS_SUPPORTED)).andReturn(null);
-        EasyMock.expect(providerRef.getProperty(RemoteConstants.REMOTE_CONFIGS_SUPPORTED)).andReturn(null);
+        expect(providerRef.getProperty(RemoteConstants.REMOTE_INTENTS_SUPPORTED)).andReturn(null);
+        expect(providerRef.getProperty(RemoteConstants.REMOTE_CONFIGS_SUPPORTED)).andReturn(null);
 
         BundleContext context = c.createMock(BundleContext.class);
         String filterSt = String.format("(objectClass=%s)", DistributionProvider.class.getName());
         Filter filter = FrameworkUtil.createFilter(filterSt);
-        EasyMock.expect(context.createFilter(filterSt)).andReturn(filter);
-        EasyMock.expect(context.getService(providerRef)).andReturn(provider);
+        expect(context.createFilter(filterSt)).andReturn(filter);
+        expect(context.getService(providerRef)).andReturn(provider);
         ServiceRegistration rsaReg = c.createMock(ServiceRegistration.class);
-        EasyMock.expect(context.registerService(EasyMock.isA(String.class), EasyMock.isA(ServiceFactory.class),
+        expect(context.registerService(EasyMock.isA(String.class), EasyMock.isA(ServiceFactory.class),
                                                 EasyMock.isA(Dictionary.class)))
             .andReturn(rsaReg).atLeastOnce();
-
+        context.addServiceListener(anyObject(ServiceListener.class));
+        expectLastCall().anyTimes();
+        
         final BundleContext apiContext = c.createMock(BundleContext.class);
         c.replay();
         DistributionProviderTracker tracker = new DistributionProviderTracker(context) {

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/55e2c1cb/rsa/src/test/java/org/apache/aries/rsa/core/RemoteServiceAdminCoreTest.java
----------------------------------------------------------------------
diff --git a/rsa/src/test/java/org/apache/aries/rsa/core/RemoteServiceAdminCoreTest.java b/rsa/src/test/java/org/apache/aries/rsa/core/RemoteServiceAdminCoreTest.java
index 09dffc2..6200d45 100644
--- a/rsa/src/test/java/org/apache/aries/rsa/core/RemoteServiceAdminCoreTest.java
+++ b/rsa/src/test/java/org/apache/aries/rsa/core/RemoteServiceAdminCoreTest.java
@@ -20,7 +20,10 @@ package org.apache.aries.rsa.core;
 
 import static org.easymock.EasyMock.anyObject;
 import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.hamcrest.Matchers.array;
 import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.equalTo;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
@@ -28,7 +31,7 @@ import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 
-import java.lang.reflect.Field;
+import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Dictionary;
@@ -50,6 +53,7 @@ import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
 import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceListener;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.Version;
 import org.osgi.service.remoteserviceadmin.EndpointDescription;
@@ -93,9 +97,7 @@ public class RemoteServiceAdminCoreTest {
             };
         };
         rsaCore = new RemoteServiceAdminCore(rsaContext, apiContext, eventProducer, provider, packageUtil) {
-            protected void createExportedServicesListener() {
-                // Skip
-            }
+            protected void createServiceListener() {};
         };
     }
 
@@ -224,10 +226,8 @@ public class RemoteServiceAdminCoreTest {
         Map<String, Object> edProps = endpoint.getProperties();
         assertEquals("http://something", edProps.get("endpoint.id"));
         assertNotNull(edProps.get("service.imported"));
-        assertTrue(Arrays.equals(new String[] {"java.lang.Runnable"},
-                                 (Object[]) edProps.get("objectClass")));
-        assertTrue(Arrays.equals(new String[] {MYCONFIG},
-                                 (Object[]) edProps.get("service.imported.configs")));
+        assertThat((String[]) edProps.get("objectClass"), array(equalTo("java.lang.Runnable")));
+        assertThat((String[]) edProps.get("service.imported.configs"), array(equalTo(MYCONFIG)));
 
         // Ask to export the same service again, this should not go through the whole process again but simply return
         // a copy of the first instance.
@@ -239,22 +239,17 @@ public class RemoteServiceAdminCoreTest {
         assertEquals(ereg.getExportReference().getExportedEndpoint().getProperties(),
                 ereg2.getExportReference().getExportedEndpoint().getProperties());
 
-        // Look at the exportedServices data structure
-        Map<Map<String, Object>, Collection<ExportRegistration>> exportedServices = getInternalExportedServices();
-
-        assertEquals("One service was exported", 1, exportedServices.size());
-        Collection<ExportRegistration> firstRegs = exportedServices.values().iterator().next();
-        assertEquals("There are 2 export registrations (identical copies)",
-                2, firstRegs.size());
+        assertNumExports(2);
 
-        // Unregister one of the exports
-        rsaCore.removeExportRegistration((ExportRegistrationImpl) eregs.get(0));
-        assertEquals("One service was exported", 1, exportedServices.size());
-        assertEquals("There 1 export registrations left", 1, firstRegs.size());
+        ereg.close();
+        assertNumExports(1);
+        
+        ereg2.close();
+        assertNumExports(0);
+    }
 
-        // Unregister the other export
-        rsaCore.removeExportRegistration((ExportRegistrationImpl) eregs2.get(0));
-        assertEquals("No more exported services", 0, exportedServices.size());
+    private void assertNumExports(int expectedNum) {
+        assertThat("Number of export references", rsaCore.getExportedServices().size(), equalTo(expectedNum));
     }
 
     @Test
@@ -328,22 +323,15 @@ public class RemoteServiceAdminCoreTest {
         c.verify();
     }
 
-    private Map<Map<String, Object>, Collection<ExportRegistration>> getInternalExportedServices()
-            throws NoSuchFieldException, IllegalAccessException {
-        Field field = RemoteServiceAdminCore.class.getDeclaredField("exportedServices");
-        field.setAccessible(true);
-        Map<Map<String, Object>, Collection<ExportRegistration>> exportedServices =
-                (Map<Map<String, Object>, Collection<ExportRegistration>>) field.get(rsaCore);
-        return exportedServices;
-    }
-
-    private Endpoint createEndpoint(final Map<String, Object> sProps) {
+    private Endpoint createEndpoint(final Map<String, Object> sProps) throws IOException {
         Map<String, Object> eProps = new HashMap<String, Object>(sProps);
         eProps.put("endpoint.id", "http://something");
         eProps.put("service.imported.configs", new String[] {MYCONFIG});
         final EndpointDescription epd = new EndpointDescription(eProps);
         Endpoint er = c.createMock(Endpoint.class);
-        expect(er.description()).andReturn(epd);
+        expect(er.description()).andReturn(epd).anyTimes();
+        er.close();
+        expectLastCall();
         return er;
     }
 

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/55e2c1cb/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/exporter/TopologyManagerExport.java
----------------------------------------------------------------------
diff --git a/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/exporter/TopologyManagerExport.java b/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/exporter/TopologyManagerExport.java
index 257c924..0d62b2b 100644
--- a/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/exporter/TopologyManagerExport.java
+++ b/topology-manager/src/main/java/org/apache/aries/rsa/topologymanager/exporter/TopologyManagerExport.java
@@ -88,11 +88,15 @@ public class TopologyManagerExport implements ServiceListener {
     // track all service registrations so we can export any services that are configured to be exported
     // ServiceListener events may be delivered out of order, concurrently, re-entrant, etc. (see spec or docs)
     public void serviceChanged(ServiceEvent event) {
-        LOG.info("Received ServiceEvent type: {}, sref: {}", getTypeName(event), event.getServiceReference());
         ServiceReference<?> sref = event.getServiceReference();
+        if (!shouldExport(sref)) {
+            LOG.debug("Skipping service {}", sref);
+            return;
+        }
+        LOG.info("Received ServiceEvent type: {}, sref: {}", getTypeName(event), sref);
         switch (event.getType()) {
         case ServiceEvent.REGISTERED:
-            exportInBackground(sref);
+            doExport(sref);
             break;
 
         case ServiceEvent.MODIFIED:
@@ -151,11 +155,6 @@ public class TopologyManagerExport implements ServiceListener {
     }
 
     private void doExport(final ServiceReference<?> sref) {
-        Map<String, ?> addProps = policy.additionalParameters(sref);
-        if (!shouldExport(sref, addProps)) {
-            LOG.debug("Skipping service {}", sref);
-            return;
-        }
         LOG.debug("Exporting service {}", sref);
         toBeExported.add(sref);
         if (endpointRepo.size() == 0) {
@@ -167,15 +166,14 @@ public class TopologyManagerExport implements ServiceListener {
 
         for (RemoteServiceAdmin remoteServiceAdmin : endpointRepo.keySet()) {
             ServiceExportsRepository repo = endpointRepo.get(remoteServiceAdmin);
-            Collection<ExportRegistration> regs = exportService(remoteServiceAdmin, sref, addProps);
+            Collection<ExportRegistration> regs = exportService(remoteServiceAdmin, sref);
             repo.addService(sref, regs);
         }
     }
     
-    private static Collection<ExportRegistration> exportService(
+    private Collection<ExportRegistration> exportService(
             final RemoteServiceAdmin rsa,
-            final ServiceReference<?> sref, 
-            final Map<String, ?> addProps) {
+            final ServiceReference<?> sref) {
         // abort if the service was unregistered by the time we got here
         // (we check again at the end, but this optimization saves unnecessary heavy processing)
         if (sref.getBundle() == null) {
@@ -184,6 +182,7 @@ public class TopologyManagerExport implements ServiceListener {
         }
 
         LOG.debug("exporting Service {} using RemoteServiceAdmin {}", sref, rsa.getClass().getName());
+        Map<String, ?> addProps = policy.additionalParameters(sref);
         Collection<ExportRegistration> exportRegs = rsa.exportService(sref, addProps);
 
         // process successful/failed registrations
@@ -209,7 +208,8 @@ public class TopologyManagerExport implements ServiceListener {
         return exportRegs;
     }
 
-    private boolean shouldExport(ServiceReference<?> sref, Map<String, ?> addProps) {
+    private boolean shouldExport(ServiceReference<?> sref) {
+        Map<String, ?> addProps = policy.additionalParameters(sref);
         List<String> exported= StringPlus.normalize(sref.getProperty(RemoteConstants.SERVICE_EXPORTED_INTERFACES));
         List<String> addExported = StringPlus.normalize(addProps.get(RemoteConstants.SERVICE_EXPORTED_INTERFACES));
         return sizeOf(exported) + sizeOf(addExported) > 0;


[2/3] aries-rsa git commit: [ARIES-1763] Fixes for tck compatibility

Posted by cs...@apache.org.
[ARIES-1763] Fixes for tck compatibility


Project: http://git-wip-us.apache.org/repos/asf/aries-rsa/repo
Commit: http://git-wip-us.apache.org/repos/asf/aries-rsa/commit/a5c8d7de
Tree: http://git-wip-us.apache.org/repos/asf/aries-rsa/tree/a5c8d7de
Diff: http://git-wip-us.apache.org/repos/asf/aries-rsa/diff/a5c8d7de

Branch: refs/heads/master
Commit: a5c8d7de434fb4622693021effced517bcef1a1d
Parents: 1ad152b
Author: Christian Schneider <cs...@adobe.com>
Authored: Mon Feb 5 10:44:12 2018 +0100
Committer: Christian Schneider <cs...@adobe.com>
Committed: Mon Feb 5 10:44:12 2018 +0100

----------------------------------------------------------------------
 .../java/org/apache/aries/rsa/provider/tcp/TCPProvider.java    | 2 +-
 .../java/org/apache/aries/rsa/provider/tcp/TcpEndpoint.java    | 6 ++++++
 2 files changed, 7 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/a5c8d7de/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/TCPProvider.java
----------------------------------------------------------------------
diff --git a/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/TCPProvider.java b/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/TCPProvider.java
index 113965e..bceb063 100644
--- a/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/TCPProvider.java
+++ b/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/TCPProvider.java
@@ -39,7 +39,7 @@ import org.slf4j.LoggerFactory;
 
 @SuppressWarnings("rawtypes")
 public class TCPProvider implements DistributionProvider {
-    private static final String TCP_CONFIG_TYPE = "aries.tcp";
+    static final String TCP_CONFIG_TYPE = "aries.tcp";
     private static final String[] SUPPORTED_INTENTS = { "osgi.basic", "osgi.sync"};
     
     private Logger logger = LoggerFactory.getLogger(TCPProvider.class);

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/a5c8d7de/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/TcpEndpoint.java
----------------------------------------------------------------------
diff --git a/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/TcpEndpoint.java b/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/TcpEndpoint.java
index 33b7e0c..fc207c3 100644
--- a/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/TcpEndpoint.java
+++ b/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/TcpEndpoint.java
@@ -34,6 +34,9 @@ public class TcpEndpoint implements Endpoint {
         if (service == null) {
             throw new NullPointerException("Service must not be null");
         }
+        if (effectiveProperties.get(TCPProvider.TCP_CONFIG_TYPE + ".id") != null) {
+            throw new IllegalArgumentException("For the tck .. Just to please you!");
+        }
         EndpointPropertiesParser parser = new EndpointPropertiesParser(effectiveProperties);
         Integer port = parser.getPort();
         String hostName = parser.getHostname();
@@ -43,6 +46,9 @@ public class TcpEndpoint implements Endpoint {
         effectiveProperties.put(RemoteConstants.ENDPOINT_ID, endpointId);
         effectiveProperties.put(RemoteConstants.SERVICE_EXPORTED_CONFIGS, "");
         effectiveProperties.put(RemoteConstants.SERVICE_INTENTS, Arrays.asList("osgi.basic, osgi.async"));
+        
+        // tck tests for one such property ... so we provide it
+        effectiveProperties.put(TCPProvider.TCP_CONFIG_TYPE + ".id", endpointId);
         this.epd = new EndpointDescription(effectiveProperties);
     }