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 2016/03/13 11:01:05 UTC

aries-rsa git commit: Add itest and fix discovery zookeeper

Repository: aries-rsa
Updated Branches:
  refs/heads/master 60e9ab9b3 -> 96a747fba


Add itest and fix discovery zookeeper


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

Branch: refs/heads/master
Commit: 96a747fba130482860fd08677e3551dea23210c7
Parents: 60e9ab9
Author: Christian Schneider <ch...@die-schneider.net>
Authored: Sun Mar 13 11:00:54 2016 +0100
Committer: Christian Schneider <ch...@die-schneider.net>
Committed: Sun Mar 13 11:00:54 2016 +0100

----------------------------------------------------------------------
 .../rsa/discovery/local/LocalDiscovery.java     |   2 +-
 .../zookeeper/server/config/Activator.java      |   4 +-
 discovery/zookeeper-server/pom.xml              |  34 ----
 .../discovery/zookeeper/server/Activator.java   |   2 +-
 discovery/zookeeper/pom.xml                     |  23 ---
 .../discovery/zookeeper/ZooKeeperDiscovery.java |  36 ++--
 .../publish/PublishingEndpointListener.java     |  25 +--
 .../PublishingEndpointListenerFactory.java      |   9 +-
 .../subscribe/InterfaceMonitorManager.java      |  29 +++-
 .../dosgi/discovery/zookeeper/util/Utils.java   |  26 ---
 .../zookeeper/ZookeeperDiscoveryTest.java       |   7 +-
 .../publish/PublishingEndpointListenerTest.java | 174 ++++++++++---------
 features/src/main/resources/features.xml        |   2 +-
 itests/felix/pom.xml                            | 139 +++++++++++++++
 .../rsa/itests/felix/TestDiscoveryExport.java   | 141 +++++++++++++++
 itests/pom.xml                                  |  78 +++++++++
 itests/testbundle-service-tcp/bnd.bnd           |   3 +
 itests/testbundle-service-tcp/pom.xml           |  40 +++++
 .../aries/rsa/itests/tcp/api/EchoService.java   |   5 +
 .../aries/rsa/itests/tcp/service/Activator.java |  25 +++
 .../rsa/itests/tcp/service/EchoServiceImpl.java |  12 ++
 parent/pom.xml                                  |  41 ++++-
 pom.xml                                         |   1 +
 23 files changed, 640 insertions(+), 218 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/96a747fb/discovery/local/src/main/java/org/apache/aries/rsa/discovery/local/LocalDiscovery.java
----------------------------------------------------------------------
diff --git a/discovery/local/src/main/java/org/apache/aries/rsa/discovery/local/LocalDiscovery.java b/discovery/local/src/main/java/org/apache/aries/rsa/discovery/local/LocalDiscovery.java
index 31f49ca..ecb30de 100644
--- a/discovery/local/src/main/java/org/apache/aries/rsa/discovery/local/LocalDiscovery.java
+++ b/discovery/local/src/main/java/org/apache/aries/rsa/discovery/local/LocalDiscovery.java
@@ -45,7 +45,7 @@ public class LocalDiscovery implements BundleListener {
 
     // this is effectively a set which allows for multiple service descriptions with the
     // same interface name but different properties and takes care of itself with respect to concurrency
-    ConcurrentHashMap<EndpointDescription, Bundle> endpointDescriptions =
+    Map<EndpointDescription, Bundle> endpointDescriptions =
         new ConcurrentHashMap<EndpointDescription, Bundle>();
     Map<EndpointListener, Collection<String>> listenerToFilters =
         new HashMap<EndpointListener, Collection<String>>();

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/96a747fb/discovery/zookeeper-server-config/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/server/config/Activator.java
----------------------------------------------------------------------
diff --git a/discovery/zookeeper-server-config/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/server/config/Activator.java b/discovery/zookeeper-server-config/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/server/config/Activator.java
index 1243818..e92fe0b 100644
--- a/discovery/zookeeper-server-config/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/server/config/Activator.java
+++ b/discovery/zookeeper-server-config/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/server/config/Activator.java
@@ -36,8 +36,8 @@ import org.slf4j.LoggerFactory;
 public class Activator implements BundleActivator {
 
     private static final Logger LOG = LoggerFactory.getLogger(Activator.class);
-    private static final String ZOOKEEPER_PORT = "org.apache.cxf.dosgi.discovery.zookeeper.port";
-    private static final String PID = "org.apache.cxf.dosgi.discovery.zookeeper.server";
+    private static final String ZOOKEEPER_PORT = "org.apache.aries.rsa.discovery.zookeeper.port";
+    private static final String PID = "org.apache.aries.rsa.discovery.zookeeper.server";
     private ServiceTracker<ConfigurationAdmin, ConfigurationAdmin> st;
 
     public void start(BundleContext context) throws Exception {

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/96a747fb/discovery/zookeeper-server/pom.xml
----------------------------------------------------------------------
diff --git a/discovery/zookeeper-server/pom.xml b/discovery/zookeeper-server/pom.xml
index 705d319..e6bcdba 100644
--- a/discovery/zookeeper-server/pom.xml
+++ b/discovery/zookeeper-server/pom.xml
@@ -42,40 +42,6 @@
         <dependency>
             <groupId>org.apache.zookeeper</groupId>
             <artifactId>zookeeper</artifactId>
-            <version>${zookeeper.version}</version>
-            <exclusions>
-                <exclusion>
-                   <groupId>com.sun.jdmk</groupId>
-                   <artifactId>jmxtools</artifactId>
-                </exclusion>
-                <exclusion>
-                   <groupId>com.sun.jmx</groupId>
-                   <artifactId>jmxri</artifactId>
-                </exclusion>
-                <exclusion>
-                    <artifactId>slf4j-log4j12</artifactId>
-                    <groupId>org.slf4j</groupId>
-                </exclusion>
-                <exclusion>
-                    <artifactId>jline</artifactId>
-                    <groupId>jline</groupId>
-                </exclusion>
-                <exclusion>
-                    <artifactId>netty</artifactId>
-                    <groupId>io.netty</groupId>
-                </exclusion>
-                <exclusion>
-                    <artifactId>log4j</artifactId>
-                    <groupId>log4j</groupId>
-                </exclusion>
-            </exclusions>
-        </dependency>
-
-        <!--  We need the newer log4j as the one from ZooKeeper has some ugly dependencies -->
-        <dependency>
-            <groupId>log4j</groupId>
-            <artifactId>log4j</artifactId>
-            <version>${log4j.version}</version>
             <scope>provided</scope>
         </dependency>
 

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/96a747fb/discovery/zookeeper-server/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/server/Activator.java
----------------------------------------------------------------------
diff --git a/discovery/zookeeper-server/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/server/Activator.java b/discovery/zookeeper-server/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/server/Activator.java
index 6adf700..17c5568 100644
--- a/discovery/zookeeper-server/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/server/Activator.java
+++ b/discovery/zookeeper-server/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/server/Activator.java
@@ -32,7 +32,7 @@ public class Activator implements BundleActivator {
     public void start(BundleContext context) throws Exception {
         zkStarter = new ZookeeperStarter(context);
         Dictionary<String, Object> props = new Hashtable<String, Object>();
-        props.put(Constants.SERVICE_PID, "org.apache.cxf.dosgi.discovery.zookeeper.server");
+        props.put(Constants.SERVICE_PID, "org.apache.aries.rsa.discovery.zookeeper.server");
         context.registerService(org.osgi.service.cm.ManagedService.class.getName(), zkStarter, props);
     }
 

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/96a747fb/discovery/zookeeper/pom.xml
----------------------------------------------------------------------
diff --git a/discovery/zookeeper/pom.xml b/discovery/zookeeper/pom.xml
index e0c9db7..0613323 100644
--- a/discovery/zookeeper/pom.xml
+++ b/discovery/zookeeper/pom.xml
@@ -45,29 +45,6 @@
         <dependency>
             <groupId>org.apache.zookeeper</groupId>
             <artifactId>zookeeper</artifactId>
-            <version>${zookeeper.version}</version>
-            <scope>provided</scope>
-            <exclusions>
-                <exclusion>
-                    <groupId>com.sun.jdmk</groupId>
-                    <artifactId>jmxtools</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>com.sun.jmx</groupId>
-                    <artifactId>jmxri</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>log4j</groupId>
-                    <artifactId>log4j</artifactId>
-                </exclusion>
-            </exclusions>
-        </dependency>
-
-        <!--  We need the newer log4j as the one from zookeeper has some ugly dependencies -->
-        <dependency>
-            <groupId>log4j</groupId>
-            <artifactId>log4j</artifactId>
-            <version>${log4j.version}</version>
             <scope>provided</scope>
         </dependency>
 

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/96a747fb/discovery/zookeeper/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/ZooKeeperDiscovery.java
----------------------------------------------------------------------
diff --git a/discovery/zookeeper/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/ZooKeeperDiscovery.java b/discovery/zookeeper/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/ZooKeeperDiscovery.java
index 6157d2a..3a7f2c4 100644
--- a/discovery/zookeeper/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/ZooKeeperDiscovery.java
+++ b/discovery/zookeeper/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/ZooKeeperDiscovery.java
@@ -49,7 +49,7 @@ public class ZooKeeperDiscovery implements Watcher, ManagedService {
     private PublishingEndpointListenerFactory endpointListenerFactory;
     private ServiceTracker<EndpointListener, EndpointListener> endpointListenerTracker;
     private InterfaceMonitorManager imManager;
-    private ZooKeeper zk;
+    private ZooKeeper zkClient;
     private boolean closed;
     private boolean started;
 
@@ -68,7 +68,11 @@ public class ZooKeeperDiscovery implements Watcher, ManagedService {
             // config is null if it doesn't exist, is being deleted or has not yet been loaded
             // in which case we just stop running
             if (!closed && configuration != null) {
-                createZookeeper(configuration);
+                try {
+                    createZookeeper(configuration);
+                } catch (IOException e) {
+                    throw new ConfigurationException(null, "Error starting zookeeper client", e);
+                }
             }
         }
     }
@@ -83,9 +87,9 @@ public class ZooKeeperDiscovery implements Watcher, ManagedService {
             return;
         }
         LOG.debug("starting ZookeeperDiscovery");
-        endpointListenerFactory = new PublishingEndpointListenerFactory(zk, bctx);
+        endpointListenerFactory = new PublishingEndpointListenerFactory(zkClient, bctx);
         endpointListenerFactory.start();
-        imManager = new InterfaceMonitorManager(bctx, zk);
+        imManager = new InterfaceMonitorManager(bctx, zkClient);
         endpointListenerTracker = new EndpointListenerTracker(bctx, imManager);
         endpointListenerTracker.open();
         started = true;
@@ -106,23 +110,19 @@ public class ZooKeeperDiscovery implements Watcher, ManagedService {
         if (imManager != null) {
             imManager.close();
         }
-        if (zk != null) {
+        if (zkClient != null) {
             try {
-                zk.close();
+                zkClient.close();
             } catch (InterruptedException e) {
                 LOG.error("Error closing ZooKeeper", e);
             }
         }
     }
 
-    protected void createZooKeeper(String host, String port, int timeout) {
-        LOG.debug("ZooKeeper configuration: connecting to {}:{} with timeout {}",
+    protected ZooKeeper createZooKeeper(String host, String port, int timeout) throws IOException {
+        LOG.info("ZooKeeper discovery connecting to {}:{} with timeout {}",
                 new Object[]{host, port, timeout});
-        try {
-            zk = new ZooKeeper(host + ":" + port, timeout, this);
-        } catch (IOException e) {
-            LOG.error("Failed to start the ZooKeeper Discovery component.", e);
-        }
+        return new ZooKeeper(host + ":" + port, timeout, this);
     }
 
     /* Callback for ZooKeeper */
@@ -139,7 +139,11 @@ public class ZooKeeperDiscovery implements Watcher, ManagedService {
         case Expired:
             LOG.info("Connection to ZooKeeper expired. Trying to create a new connection");
             stop(false);
-            createZookeeper(curConfiguration);
+            try {
+                createZookeeper(curConfiguration);
+            } catch (IOException e) {
+                LOG.error("Error starting zookeeper client", e);
+            }
             break;
 
         default:
@@ -148,11 +152,11 @@ public class ZooKeeperDiscovery implements Watcher, ManagedService {
         }
     }
 
-    private void createZookeeper(Dictionary<String, ?> config) {
+    private void createZookeeper(Dictionary<String, ?> config) throws IOException {
         String host = (String)getWithDefault(config, "zookeeper.host", "localhost");
         String port = (String)getWithDefault(config, "zookeeper.port", "2181");
         int timeout = Integer.parseInt((String)getWithDefault(config, "zookeeper.timeout", "3000"));
-        createZooKeeper(host, port, timeout);
+        zkClient = createZooKeeper(host, port, timeout);
     }
     
     public Object getWithDefault(Dictionary<String, ?> config, String key, Object defaultValue) {

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/96a747fb/discovery/zookeeper/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/publish/PublishingEndpointListener.java
----------------------------------------------------------------------
diff --git a/discovery/zookeeper/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/publish/PublishingEndpointListener.java b/discovery/zookeeper/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/publish/PublishingEndpointListener.java
index d90e355..9bcfe72 100644
--- a/discovery/zookeeper/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/publish/PublishingEndpointListener.java
+++ b/discovery/zookeeper/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/publish/PublishingEndpointListener.java
@@ -93,7 +93,7 @@ public class PublishingEndpointListener implements EndpointListener {
     private void addEndpoint(EndpointDescription endpoint) throws URISyntaxException, KeeperException,
                                                                   InterruptedException, IOException {
         Collection<String> interfaces = endpoint.getInterfaces();
-        String endpointKey = getKey(endpoint.getId());
+        String endpointKey = getKey(endpoint);
         Map<String, Object> props = new HashMap<String, Object>(endpoint.getProperties());
 
         // process plugins
@@ -109,8 +109,8 @@ public class PublishingEndpointListener implements EndpointListener {
         for (String name : interfaces) {
             String path = Utils.getZooKeeperPath(name);
             String fullPath = path + '/' + endpointKey;
-            LOG.debug("Creating ZooKeeper node: {}", fullPath);
-            ensurePath(path, zk);
+            LOG.info("Creating ZooKeeper node for service with path {}", fullPath);
+            createPath(path, zk);
             List<PropertyType> propsOut = new PropertiesMapper().fromProps(props);
             EndpointDescriptionType epd = new EndpointDescriptionType();
             epd.getProperty().addAll(propsOut);
@@ -159,8 +159,7 @@ public class PublishingEndpointListener implements EndpointListener {
 
     private void removeEndpoint(EndpointDescription endpoint) throws UnknownHostException, URISyntaxException {
         Collection<String> interfaces = endpoint.getInterfaces();
-        String endpointKey = getKey(endpoint.getId());
-
+        String endpointKey = getKey(endpoint);
         for (String name : interfaces) {
             String path = Utils.getZooKeeperPath(name);
             String fullPath = path + '/' + endpointKey;
@@ -173,7 +172,7 @@ public class PublishingEndpointListener implements EndpointListener {
         }
     }
 
-    private static void ensurePath(String path, ZooKeeper zk) throws KeeperException, InterruptedException {
+    private static void createPath(String path, ZooKeeper zk) throws KeeperException, InterruptedException {
         StringBuilder current = new StringBuilder();
         List<String> parts = Utils.removeEmpty(Arrays.asList(path.split("/")));
         for (String part : parts) {
@@ -187,16 +186,10 @@ public class PublishingEndpointListener implements EndpointListener {
         }
     }
 
-    static String getKey(String endpoint) throws URISyntaxException {
-        URI uri = new URI(endpoint);
-
-        StringBuilder sb = new StringBuilder();
-        sb.append(uri.getHost());
-        sb.append("#");
-        sb.append(uri.getPort());
-        sb.append("#");
-        sb.append(uri.getPath().replace('/', '#'));
-        return sb.toString();
+    private static String getKey(EndpointDescription endpoint) throws URISyntaxException {
+        URI uri = new URI(endpoint.getId());
+        return new StringBuilder().append(uri.getHost()).append("#").append(uri.getPort())
+            .append("#").append(uri.getPath().replace('/', '#')).toString();
     }
 
     public void close() {

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/96a747fb/discovery/zookeeper/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/publish/PublishingEndpointListenerFactory.java
----------------------------------------------------------------------
diff --git a/discovery/zookeeper/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/publish/PublishingEndpointListenerFactory.java b/discovery/zookeeper/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/publish/PublishingEndpointListenerFactory.java
index bbb72d7..99a9849 100644
--- a/discovery/zookeeper/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/publish/PublishingEndpointListenerFactory.java
+++ b/discovery/zookeeper/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/publish/PublishingEndpointListenerFactory.java
@@ -73,9 +73,10 @@ public class PublishingEndpointListenerFactory implements ServiceFactory<Publish
 
     public synchronized void start() {
         Dictionary<String, String> props = new Hashtable<String, String>();
+        String uuid = bctx.getProperty(Constants.FRAMEWORK_UUID);
         props.put(EndpointListener.ENDPOINT_LISTENER_SCOPE, 
-                  String.format("(&(%s=*)(%s=))", Constants.OBJECTCLASS, 
-                                RemoteConstants.ENDPOINT_FRAMEWORK_UUID, getUUID(bctx)));
+                  String.format("(&(%s=*)(%s=%s))", Constants.OBJECTCLASS, 
+                                RemoteConstants.ENDPOINT_FRAMEWORK_UUID, uuid));
         props.put(ZooKeeperDiscovery.DISCOVERY_ZOOKEEPER_ID, "true");
         serviceRegistration = bctx.registerService(EndpointListener.class.getName(), this, props);
     }
@@ -92,10 +93,6 @@ public class PublishingEndpointListenerFactory implements ServiceFactory<Publish
             listeners.clear();
         }
     }
-    
-    private String getUUID(BundleContext bc) {
-        return bc.getProperty(Constants.FRAMEWORK_UUID);
-    }
 
     /**
      * Only for the test case!

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/96a747fb/discovery/zookeeper/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/subscribe/InterfaceMonitorManager.java
----------------------------------------------------------------------
diff --git a/discovery/zookeeper/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/subscribe/InterfaceMonitorManager.java b/discovery/zookeeper/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/subscribe/InterfaceMonitorManager.java
index 1a23a93..f44b5af 100644
--- a/discovery/zookeeper/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/subscribe/InterfaceMonitorManager.java
+++ b/discovery/zookeeper/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/subscribe/InterfaceMonitorManager.java
@@ -26,6 +26,8 @@ import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import org.apache.aries.rsa.util.StringPlus;
 import org.apache.cxf.dosgi.discovery.zookeeper.ZooKeeperDiscovery;
@@ -47,8 +49,8 @@ import org.slf4j.LoggerFactory;
  * These events are then forwarded to all interested EndpointListeners.
  */
 public class InterfaceMonitorManager {
-
     private static final Logger LOG = LoggerFactory.getLogger(InterfaceMonitorManager.class);
+    private static final Pattern OBJECTCLASS_PATTERN = Pattern.compile(".*\\(objectClass=([^)]+)\\).*");
 
     private final BundleContext bctx;
     private final ZooKeeper zk;
@@ -77,10 +79,10 @@ public class InterfaceMonitorManager {
 
         LOG.info("updating EndpointListener interests: {}", endpointListener);
         if (LOG.isDebugEnabled()) {
-            LOG.debug("updated EndpointListener properties: {}", Utils.getProperties(endpointListener));
+            LOG.debug("updated EndpointListener properties: {}", getProperties(endpointListener));
         }
         for (String scope : getScopes(endpointListener)) {
-            String objClass = Utils.getObjectClass(scope);
+            String objClass = getObjectClass(scope);
             LOG.debug("Adding interest in scope {}, objectClass {}", scope, objClass);
             addInterest(endpointListener, scope, objClass);
         }
@@ -235,4 +237,25 @@ public class InterfaceMonitorManager {
     protected List<String> getScopes(ServiceReference<?> sref) {
         return Utils.removeEmpty(StringPlus.normalize(sref.getProperty(EndpointListener.ENDPOINT_LISTENER_SCOPE)));
     }
+    
+    public static String getObjectClass(String scope) {
+        Matcher m = OBJECTCLASS_PATTERN.matcher(scope);
+        return m.matches() ? m.group(1) : null;
+    }
+
+    /**
+     * Returns a service's properties as a map.
+     *
+     * @param serviceReference a service reference
+     * @return the service's properties as a map
+     */
+    public static Map<String, Object> getProperties(ServiceReference<?> serviceReference) {
+        String[] keys = serviceReference.getPropertyKeys();
+        Map<String, Object> props = new HashMap<String, Object>(keys.length);
+        for (String key : keys) {
+            Object val = serviceReference.getProperty(key);
+            props.put(key, val);
+        }
+        return props;
+    }
 }

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/96a747fb/discovery/zookeeper/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/util/Utils.java
----------------------------------------------------------------------
diff --git a/discovery/zookeeper/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/util/Utils.java b/discovery/zookeeper/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/util/Utils.java
index d732a62..afd9c0a 100644
--- a/discovery/zookeeper/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/util/Utils.java
+++ b/discovery/zookeeper/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/util/Utils.java
@@ -19,18 +19,11 @@
 package org.apache.cxf.dosgi.discovery.zookeeper.util;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.osgi.framework.ServiceReference;
 
 public final class Utils {
 
     static final String PATH_PREFIX = "/osgi/service_registry";
-    static final Pattern OBJECTCLASS_PATTERN = Pattern.compile(".*\\(objectClass=([^)]+)\\).*");
 
     private Utils() {
         // never constructed
@@ -57,24 +50,5 @@ public final class Utils {
         return result;
     }
 
-    public static String getObjectClass(String scope) {
-        Matcher m = OBJECTCLASS_PATTERN.matcher(scope);
-        return m.matches() ? m.group(1) : null;
-    }
 
-    /**
-     * Returns a service's properties as a map.
-     *
-     * @param serviceReference a service reference
-     * @return the service's properties as a map
-     */
-    public static Map<String, Object> getProperties(ServiceReference<?> serviceReference) {
-        String[] keys = serviceReference.getPropertyKeys();
-        Map<String, Object> props = new HashMap<String, Object>(keys.length);
-        for (String key : keys) {
-            Object val = serviceReference.getProperty(key);
-            props.put(key, val);
-        }
-        return props;
-    }
 }

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/96a747fb/discovery/zookeeper/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/ZookeeperDiscoveryTest.java
----------------------------------------------------------------------
diff --git a/discovery/zookeeper/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/ZookeeperDiscoveryTest.java b/discovery/zookeeper/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/ZookeeperDiscoveryTest.java
index 6fd0aa1..ed38d5d 100644
--- a/discovery/zookeeper/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/ZookeeperDiscoveryTest.java
+++ b/discovery/zookeeper/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/ZookeeperDiscoveryTest.java
@@ -3,6 +3,7 @@ package org.apache.cxf.dosgi.discovery.zookeeper;
 import java.util.Dictionary;
 import java.util.Hashtable;
 
+import org.apache.zookeeper.ZooKeeper;
 import org.easymock.EasyMock;
 import org.easymock.IMocksControl;
 import org.junit.Assert;
@@ -19,10 +20,11 @@ public class ZookeeperDiscoveryTest {
         BundleContext bctx = c.createMock(BundleContext.class);
         ZooKeeperDiscovery zkd = new ZooKeeperDiscovery(bctx) {
             @Override
-            protected void createZooKeeper(String host, String port, int timeout) {
+            protected ZooKeeper createZooKeeper(String host, String port, int timeout) {
                 Assert.assertEquals("localhost", host);
                 Assert.assertEquals("2181", port);
                 Assert.assertEquals(3000, timeout);
+                return null;
             }  
         };
         
@@ -36,10 +38,11 @@ public class ZookeeperDiscoveryTest {
         BundleContext bctx = c.createMock(BundleContext.class);
         ZooKeeperDiscovery zkd = new ZooKeeperDiscovery(bctx) {
             @Override
-            protected void createZooKeeper(String host, String port, int timeout) {
+            protected ZooKeeper createZooKeeper(String host, String port, int timeout) {
                 Assert.assertEquals("myhost", host);
                 Assert.assertEquals("1", port);
                 Assert.assertEquals(1000, timeout);
+                return null;
             }  
         };
         

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/96a747fb/discovery/zookeeper/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/publish/PublishingEndpointListenerTest.java
----------------------------------------------------------------------
diff --git a/discovery/zookeeper/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/publish/PublishingEndpointListenerTest.java b/discovery/zookeeper/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/publish/PublishingEndpointListenerTest.java
index 31c2802..0c78d4a 100644
--- a/discovery/zookeeper/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/publish/PublishingEndpointListenerTest.java
+++ b/discovery/zookeeper/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/publish/PublishingEndpointListenerTest.java
@@ -18,6 +18,8 @@
  */
 package org.apache.cxf.dosgi.discovery.zookeeper.publish;
 
+import static org.easymock.EasyMock.expect;
+
 import java.lang.reflect.Field;
 import java.util.HashMap;
 import java.util.List;
@@ -36,6 +38,7 @@ import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
 import org.osgi.framework.Filter;
 import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceListener;
 import org.osgi.framework.ServiceReference;
 import org.osgi.service.remoteserviceadmin.EndpointDescription;
@@ -47,36 +50,24 @@ import junit.framework.TestCase;
 
 public class PublishingEndpointListenerTest extends TestCase {
 
+    private static final String ENDPOINT_PATH = "/osgi/service_registry/myClass/google.de#80##test#sub";
+
     public void testEndpointRemovalAdding() throws KeeperException, InterruptedException {
         IMocksControl c = EasyMock.createNiceControl();
 
         BundleContext ctx = c.createMock(BundleContext.class);
         ZooKeeper zk = c.createMock(ZooKeeper.class);
 
-        String path = "/osgi/service_registry/myClass/google.de#80##test";
-        EasyMock.expect(zk.create(EasyMock.eq(path),
-                (byte[])EasyMock.anyObject(), EasyMock.eq(Ids.OPEN_ACL_UNSAFE),
-                EasyMock.eq(CreateMode.EPHEMERAL))).andReturn("").once();
-
-        zk.delete(EasyMock.eq("/osgi/service_registry/myClass/google.de#80##test"), EasyMock.eq(-1));
-        EasyMock.expectLastCall().once();
+        String path = ENDPOINT_PATH;
+        expectCreated(zk, path);
+        expectDeleted(zk, path);
 
         c.replay();
 
         PublishingEndpointListener eli = new PublishingEndpointListener(zk, ctx);
-
-        Map<String, Object> props = new HashMap<String, Object>();
-        props.put(Constants.OBJECTCLASS, new String[] {
-            "myClass"
-        });
-        props.put(RemoteConstants.ENDPOINT_ID, "http://google.de:80/test");
-        props.put(RemoteConstants.SERVICE_IMPORTED_CONFIGS, "myConfig");
-
-        EndpointDescription endpoint = new EndpointDescription(props);
-
+        EndpointDescription endpoint = createEndpoint();
         eli.endpointAdded(endpoint, null);
         eli.endpointAdded(endpoint, null); // should do nothing
-
         eli.endpointRemoved(endpoint, null);
         eli.endpointRemoved(endpoint, null); // should do nothing
 
@@ -84,47 +75,22 @@ public class PublishingEndpointListenerTest extends TestCase {
     }
 
     public void testDiscoveryPlugin() throws Exception {
-        DiscoveryPlugin plugin1 = new DiscoveryPlugin() {
-            public String process(Map<String, Object> mutableProperties, String endpointKey) {
-                String eid = (String) mutableProperties.get("endpoint.id");
-                mutableProperties.put("endpoint.id", eid + "/appended");
-                return endpointKey;
-            }
-        };
-        @SuppressWarnings("unchecked")
-        ServiceReference<DiscoveryPlugin> sr1 = EasyMock.createMock(ServiceReference.class);
-
-        DiscoveryPlugin plugin2 = new DiscoveryPlugin() {
-            public String process(Map<String, Object> mutableProperties, String endpointKey) {
-                mutableProperties.put("foo", "bar");
-                return endpointKey.replaceAll("localhost", "some.machine");
-            }
-        };
-        @SuppressWarnings("unchecked")
-        ServiceReference<DiscoveryPlugin> sr2 = EasyMock.createMock(ServiceReference.class);
-
         BundleContext ctx = EasyMock.createMock(BundleContext.class);
-        EasyMock.expect(ctx.createFilter(EasyMock.isA(String.class))).andAnswer(new IAnswer<Filter>() {
-            public Filter answer() throws Throwable {
-                return FrameworkUtil.createFilter((String) EasyMock.getCurrentArguments()[0]);
-            }
-        }).anyTimes();
+        stubCreateFilter(ctx);
         ctx.addServiceListener(EasyMock.isA(ServiceListener.class),
                 EasyMock.eq("(objectClass=" + DiscoveryPlugin.class.getName() + ")"));
-        EasyMock.expect(ctx.getService(sr1)).andReturn(plugin1).anyTimes();
-        EasyMock.expect(ctx.getService(sr2)).andReturn(plugin2).anyTimes();
+
+        ServiceReference<DiscoveryPlugin> sr1 = createAppendPlugin(ctx);
+        ServiceReference<DiscoveryPlugin> sr2 = createPropertyPlugin(ctx);
+
         EasyMock.expect(ctx.getServiceReferences(DiscoveryPlugin.class.getName(), null))
                 .andReturn(new ServiceReference[]{sr1, sr2}).anyTimes();
         EasyMock.replay(ctx);
 
-        Map<String, Object> props = new HashMap<String, Object>();
-        props.put(Constants.OBJECTCLASS, new String[] {"org.foo.myClass"});
-        props.put(RemoteConstants.ENDPOINT_ID, "http://localhost:9876/test");
-        props.put(RemoteConstants.SERVICE_IMPORTED_CONFIGS, "myConfig");
-        EndpointDescription endpoint = new EndpointDescription(props);
+        EndpointDescription endpoint = createEndpoint();
 
-        Map<String, Object> expectedProps = new HashMap<String, Object>(props);
-        expectedProps.put("endpoint.id", "http://localhost:9876/test/appended");
+        Map<String, Object> expectedProps = new HashMap<String, Object>(endpoint.getProperties());
+        expectedProps.put("endpoint.id", "http://google.de:80/test/sub/appended");
         expectedProps.put("foo", "bar");
         expectedProps.put("service.imported", "true");
 
@@ -135,11 +101,7 @@ public class PublishingEndpointListenerTest extends TestCase {
         EndpointDescriptionType epd = new EndpointDescriptionType();
         epd.getProperty().addAll(props2);
         byte[] data = new EndpointDescriptionParser().getData(epd);
-        EasyMock.expect(zk.create(
-                EasyMock.eq(expectedFullPath),
-                EasyMock.aryEq(data),
-                EasyMock.eq(Ids.OPEN_ACL_UNSAFE),
-                EasyMock.eq(CreateMode.EPHEMERAL))).andReturn("");
+        expectCreated(zk, expectedFullPath, EasyMock.aryEq(data));
         EasyMock.replay(zk);
 
         PublishingEndpointListener eli = new PublishingEndpointListener(zk, ctx);
@@ -153,49 +115,93 @@ public class PublishingEndpointListenerTest extends TestCase {
         //EasyMock.verify(zk);
     }
 
-    @SuppressWarnings("unchecked")
-    private List<EndpointDescription> getEndpoints(PublishingEndpointListener eli) throws Exception {
-        Field field = eli.getClass().getDeclaredField("endpoints");
-        field.setAccessible(true);
-        return (List<EndpointDescription>) field.get(eli);
-    }
+
 
     public void testClose() throws KeeperException, InterruptedException {
         IMocksControl c = EasyMock.createNiceControl();
-
         BundleContext ctx = c.createMock(BundleContext.class);
         ZooKeeper zk = c.createMock(ZooKeeper.class);
-
-        String path = "/osgi/service_registry/myClass/google.de#80##test";
-        EasyMock.expect(zk.create(EasyMock.eq(path),
-                (byte[])EasyMock.anyObject(), EasyMock.eq(Ids.OPEN_ACL_UNSAFE),
-                EasyMock.eq(CreateMode.EPHEMERAL))).andReturn("").once();
-
-        zk.delete(EasyMock.eq("/osgi/service_registry/myClass/google.de#80##test"), EasyMock.eq(-1));
-        EasyMock.expectLastCall().once();
+        expectCreated(zk, ENDPOINT_PATH);
+        expectDeleted(zk, ENDPOINT_PATH);
 
         c.replay();
 
         PublishingEndpointListener eli = new PublishingEndpointListener(zk, ctx);
+        EndpointDescription endpoint = createEndpoint();
+        eli.endpointAdded(endpoint, null);
+        eli.close(); // should result in zk.delete(...)
 
-        Map<String, Object> props = new HashMap<String, Object>();
-        props.put(Constants.OBJECTCLASS, new String[] {
-            "myClass"
-        });
-        props.put(RemoteConstants.ENDPOINT_ID, "http://google.de:80/test");
-        props.put(RemoteConstants.SERVICE_IMPORTED_CONFIGS, "myConfig");
+        c.verify();
+    }
 
-        EndpointDescription endpoint = new EndpointDescription(props);
+    @SuppressWarnings("unchecked")
+    private ServiceReference<DiscoveryPlugin> createAppendPlugin(BundleContext ctx) {
+        DiscoveryPlugin plugin1 = new DiscoveryPlugin() {
+            public String process(Map<String, Object> mutableProperties, String endpointKey) {
+                String eid = (String) mutableProperties.get("endpoint.id");
+                mutableProperties.put("endpoint.id", eid + "/appended");
+                return endpointKey;
+            }
+        };
+        ServiceReference<DiscoveryPlugin> sr1 = EasyMock.createMock(ServiceReference.class);
+        EasyMock.expect(ctx.getService(sr1)).andReturn(plugin1).anyTimes();
+        return sr1;
+    }
 
-        eli.endpointAdded(endpoint, null);
+    @SuppressWarnings("unchecked")
+    private ServiceReference<DiscoveryPlugin> createPropertyPlugin(BundleContext ctx) {
+        DiscoveryPlugin plugin2 = new DiscoveryPlugin() {
+            public String process(Map<String, Object> mutableProperties, String endpointKey) {
+                mutableProperties.put("foo", "bar");
+                return endpointKey.replaceAll("localhost", "some.machine");
+            }
+        };
+        ServiceReference<DiscoveryPlugin> sr2 = EasyMock.createMock(ServiceReference.class);
+        EasyMock.expect(ctx.getService(sr2)).andReturn(plugin2).anyTimes();
+        return sr2;
+    }
 
-        eli.close(); // should result in zk.delete(...)
+    @SuppressWarnings("unchecked")
+    private List<EndpointDescription> getEndpoints(PublishingEndpointListener eli) throws Exception {
+        Field field = eli.getClass().getDeclaredField("endpoints");
+        field.setAccessible(true);
+        return (List<EndpointDescription>) field.get(eli);
+    }
 
-        c.verify();
+    private void stubCreateFilter(BundleContext ctx) throws InvalidSyntaxException {
+        EasyMock.expect(ctx.createFilter(EasyMock.isA(String.class))).andAnswer(new IAnswer<Filter>() {
+            public Filter answer() throws Throwable {
+                return FrameworkUtil.createFilter((String) EasyMock.getCurrentArguments()[0]);
+            }
+        }).anyTimes();
+    }
+
+    private void expectCreated(ZooKeeper zk, String path, byte[] dataMatcher) throws KeeperException, InterruptedException {
+        expect(zk.create(EasyMock.eq(path), 
+                         dataMatcher, 
+                         EasyMock.eq(Ids.OPEN_ACL_UNSAFE),
+                         EasyMock.eq(CreateMode.EPHEMERAL)))
+            .andReturn("");
+    }
+    
+    private void expectCreated(ZooKeeper zk, String path) throws KeeperException, InterruptedException {
+        expect(zk.create(EasyMock.eq(path), 
+                         (byte[])EasyMock.anyObject(), 
+                         EasyMock.eq(Ids.OPEN_ACL_UNSAFE),
+                         EasyMock.eq(CreateMode.EPHEMERAL)))
+            .andReturn("");
+    }
+
+    private void expectDeleted(ZooKeeper zk, String path) throws InterruptedException, KeeperException {
+        zk.delete(EasyMock.eq(path), EasyMock.eq(-1));
+        EasyMock.expectLastCall().once();
     }
 
-    public void testGetKey() throws Exception {
-        assertEquals("somehost#9090##org#example#TestEndpoint",
-            PublishingEndpointListener.getKey("http://somehost:9090/org/example/TestEndpoint"));
+    private EndpointDescription createEndpoint() {
+        Map<String, Object> props = new HashMap<String, Object>();
+        props.put(Constants.OBJECTCLASS, new String[] {"myClass"});
+        props.put(RemoteConstants.ENDPOINT_ID, "http://google.de:80/test/sub");
+        props.put(RemoteConstants.SERVICE_IMPORTED_CONFIGS, "myConfig");
+        return new EndpointDescription(props);
     }
 }

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/96a747fb/features/src/main/resources/features.xml
----------------------------------------------------------------------
diff --git a/features/src/main/resources/features.xml b/features/src/main/resources/features.xml
index ac5bf43..d4325fa 100644
--- a/features/src/main/resources/features.xml
+++ b/features/src/main/resources/features.xml
@@ -13,7 +13,7 @@
     </feature>
     
     <feature name="aries-rsa-discovery-local" version="${project.version}">
-        <feature>caries-rsa-core</feature>
+        <feature>aries-rsa-core</feature>
         <bundle>mvn:org.apache.aries.rsa.discovery/local/${project.version}</bundle>
     </feature>
 

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/96a747fb/itests/felix/pom.xml
----------------------------------------------------------------------
diff --git a/itests/felix/pom.xml b/itests/felix/pom.xml
new file mode 100644
index 0000000..578cf69
--- /dev/null
+++ b/itests/felix/pom.xml
@@ -0,0 +1,139 @@
+<?xml version='1.0' encoding='UTF-8' ?>
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.aries.rsa</groupId>
+        <artifactId>itests</artifactId>
+        <version>1.8-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <groupId>org.apache.aries.rsa.itests</groupId>
+    <artifactId>felix</artifactId>
+    <packaging>jar</packaging>
+
+    <name>Aries Remote Service Admin itests felix</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.configadmin</artifactId>
+            <version>1.8.8</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.aries.rsa</groupId>
+            <artifactId>core</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.aries.rsa</groupId>
+            <artifactId>spi</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.aries.rsa</groupId>
+            <artifactId>topology-manager</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.aries.rsa.discovery</groupId>
+            <artifactId>local</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.aries.rsa.discovery</groupId>
+            <artifactId>zookeeper</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.aries.rsa.discovery</groupId>
+            <artifactId>zookeeper-server</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.aries.rsa.provider</groupId>
+            <artifactId>tcp</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.aries.rsa.itests</groupId>
+            <artifactId>testbundle-tcp-service</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.geronimo.specs</groupId>
+            <artifactId>geronimo-atinject_1.0_spec</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-junit4</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-inject</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-container-native</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-link-mvn</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.ops4j.pax.url</groupId>
+            <artifactId>pax-url-aether</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.framework</artifactId>
+            <version>5.0.1</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.zookeeper</groupId>
+            <artifactId>zookeeper</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.servicemix.tooling</groupId>
+                <artifactId>depends-maven-plugin</artifactId>
+                <version>1.3.1</version>
+                <executions>
+                    <execution>
+                        <id>generate-depends-file</id>
+                        <goals>
+                            <goal>generate-depends-file</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/96a747fb/itests/felix/src/test/java/org/apache/aries/rsa/itests/felix/TestDiscoveryExport.java
----------------------------------------------------------------------
diff --git a/itests/felix/src/test/java/org/apache/aries/rsa/itests/felix/TestDiscoveryExport.java b/itests/felix/src/test/java/org/apache/aries/rsa/itests/felix/TestDiscoveryExport.java
new file mode 100644
index 0000000..5d00bc4
--- /dev/null
+++ b/itests/felix/src/test/java/org/apache/aries/rsa/itests/felix/TestDiscoveryExport.java
@@ -0,0 +1,141 @@
+package org.apache.aries.rsa.itests.felix;
+/**
+ * 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.
+ */
+
+
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.systemProperty;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import javax.inject.Inject;
+
+import org.apache.zookeeper.WatchedEvent;
+import org.apache.zookeeper.Watcher;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.data.Stat;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.CoreOptions;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+@RunWith(PaxExam.class)
+public class TestDiscoveryExport {
+
+    private final class DummyWatcher implements Watcher {
+        @Override
+        public void process(WatchedEvent event) {
+        }
+    }
+
+    private static final String GREETER_ZOOKEEPER_NODE = "/osgi/service_registry/org/apache/aries/rsa/itests/tcp/api/EchoService";
+
+    @Inject
+    BundleContext bundleContext;
+
+    @Inject
+    ConfigurationAdmin configAdmin;
+
+    @Configuration
+    public static Option[] configure() throws Exception {
+        return new Option[] {
+                CoreOptions.junitBundles(),
+                systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("INFO"),
+                mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.configadmin").versionAsInProject(),
+                mavenBundle().groupId("org.apache.aries.rsa").artifactId("core").versionAsInProject(),
+                mavenBundle().groupId("org.apache.aries.rsa").artifactId("spi").versionAsInProject(),
+                mavenBundle().groupId("org.apache.aries.rsa").artifactId("topology-manager").versionAsInProject(),
+                mavenBundle().groupId("org.apache.aries.rsa.provider").artifactId("tcp").versionAsInProject(),
+                mavenBundle().groupId("org.apache.aries.rsa.discovery").artifactId("local").versionAsInProject(),
+                mavenBundle().groupId("org.apache.zookeeper").artifactId("zookeeper").versionAsInProject(),
+                mavenBundle().groupId("org.apache.aries.rsa.discovery").artifactId("zookeeper").versionAsInProject(),
+                mavenBundle().groupId("org.apache.aries.rsa.discovery").artifactId("zookeeper-server").versionAsInProject(),
+                mavenBundle().groupId("org.apache.aries.rsa.itests").artifactId("testbundle-tcp-service").versionAsInProject(),
+//              
+                //CoreOptions.vmOption("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005")
+        };
+    }
+    
+    public void testInstalled() throws Exception {
+        for (Bundle bundle : bundleContext.getBundles()) {
+            System.out.println(bundle.getBundleId() + " " + bundle.getSymbolicName() + " " + bundle.getState() + " " + bundle.getVersion());
+        }
+    }
+
+    @Test
+    public void testDiscoveryExport() throws Exception {
+        final int zkPort = 12051;
+        //getFreePort(); does not seem to work 
+        System.out.println("*** Port for ZooKeeper Server: " + zkPort);
+        updateZkServerConfig(zkPort, configAdmin);
+        Thread.sleep(1000); // To avoid exceptions in clients
+        updateZkClientConfig(zkPort, configAdmin);
+        ZooKeeper zk = new ZooKeeper("localhost:" + zkPort, 1000, new DummyWatcher());
+        assertNodeExists(zk, GREETER_ZOOKEEPER_NODE, 10000);
+        zk.close();
+    }
+
+    private void assertNodeExists(ZooKeeper zk, String zNode, int timeout) {
+        long endTime = System.currentTimeMillis() + timeout;
+        Stat stat = null;
+        while (stat == null && System.currentTimeMillis() < endTime) {
+            try {
+                stat = zk.exists(zNode, null);
+                Thread.sleep(200);
+            } catch (Exception e) {
+                // Ignore
+            }
+        }
+        Assert.assertNotNull("ZooKeeper node " + zNode + " was not found", stat);
+    }
+
+    protected void updateZkClientConfig(final int zkPort, ConfigurationAdmin cadmin) throws IOException {
+        Dictionary<String, Object> cliProps = new Hashtable<String, Object>();
+        cliProps.put("zookeeper.host", "127.0.0.1");
+        cliProps.put("zookeeper.port", "" + zkPort);
+        cadmin.getConfiguration("org.apache.aries.rsa.discovery.zookeeper", null).update(cliProps);
+    }
+
+    protected void updateZkServerConfig(final int zkPort, ConfigurationAdmin cadmin) throws IOException {
+        Dictionary<String, Object> svrProps = new Hashtable<String, Object>();
+        svrProps.put("clientPort", zkPort);
+        cadmin.getConfiguration("org.apache.aries.rsa.discovery.zookeeper.server", null).update(svrProps);
+    }
+    
+    protected int getFreePort() throws IOException {
+        ServerSocket socket = new ServerSocket();
+        try {
+            socket.setReuseAddress(true); // enables quickly reopening socket on same port
+            socket.bind(new InetSocketAddress(0)); // zero finds a free port
+            return socket.getLocalPort();
+        } finally {
+            socket.close();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/96a747fb/itests/pom.xml
----------------------------------------------------------------------
diff --git a/itests/pom.xml b/itests/pom.xml
new file mode 100644
index 0000000..7a6b227
--- /dev/null
+++ b/itests/pom.xml
@@ -0,0 +1,78 @@
+<?xml version='1.0' encoding='UTF-8' ?>
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.aries.rsa</groupId>
+        <artifactId>parent</artifactId>
+        <version>1.8-SNAPSHOT</version>
+        <relativePath>../parent/pom.xml</relativePath>
+    </parent>
+
+    <artifactId>itests</artifactId>
+    <packaging>pom</packaging>
+
+    <name>Aries Remote Service Admin itests</name>
+
+    <properties>
+        <topDirectoryLocation>..</topDirectoryLocation>
+        <pax-exam.version>4.8.0</pax-exam.version>
+    </properties>
+
+    <modules>
+        <module>felix</module>
+    </modules>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.apache.geronimo.specs</groupId>
+                <artifactId>geronimo-atinject_1.0_spec</artifactId>
+                <version>1.0</version>
+            </dependency>
+            <dependency>
+                <groupId>org.ops4j.pax.exam</groupId>
+                <artifactId>pax-exam-junit4</artifactId>
+                <version>${pax-exam.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.ops4j.pax.exam</groupId>
+                <artifactId>pax-exam-inject</artifactId>
+                <version>${pax-exam.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.ops4j.pax.exam</groupId>
+                <artifactId>pax-exam-container-forked</artifactId>
+                <version>${pax-exam.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.ops4j.pax.exam</groupId>
+                <artifactId>pax-exam-container-native</artifactId>
+                <version>${pax-exam.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.ops4j.pax.exam</groupId>
+                <artifactId>pax-exam-link-mvn</artifactId>
+                <version>${pax-exam.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.ops4j.pax.url</groupId>
+                <artifactId>pax-url-aether</artifactId>
+                <version>2.4.5</version>
+            </dependency>
+        </dependencies>
+
+    </dependencyManagement>
+</project>

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/96a747fb/itests/testbundle-service-tcp/bnd.bnd
----------------------------------------------------------------------
diff --git a/itests/testbundle-service-tcp/bnd.bnd b/itests/testbundle-service-tcp/bnd.bnd
new file mode 100644
index 0000000..b0cfcc1
--- /dev/null
+++ b/itests/testbundle-service-tcp/bnd.bnd
@@ -0,0 +1,3 @@
+Bundle-Activator: org.apache.aries.rsa.itests.tcp.service.Activator
+Export-Package: org.apache.aries.rsa.itests.tcp.api
+Private-Package: org.apache.aries.rsa.itests.tcp.service
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/96a747fb/itests/testbundle-service-tcp/pom.xml
----------------------------------------------------------------------
diff --git a/itests/testbundle-service-tcp/pom.xml b/itests/testbundle-service-tcp/pom.xml
new file mode 100644
index 0000000..0fd4d3c
--- /dev/null
+++ b/itests/testbundle-service-tcp/pom.xml
@@ -0,0 +1,40 @@
+<?xml version='1.0' encoding='UTF-8' ?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+      <groupId>org.apache.aries.rsa</groupId>
+      <artifactId>itests</artifactId>
+      <version>1.8-SNAPSHOT</version>
+      <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <groupId>org.apache.aries.rsa.itests</groupId>
+    <artifactId>testbundle-tcp-service</artifactId>
+    <packaging>bundle</packaging>
+
+    <name>Aries Remote Service Admin itests testbundle service tcp</name>
+
+    <dependencies>
+    </dependencies>
+
+</project>

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/96a747fb/itests/testbundle-service-tcp/src/main/java/org/apache/aries/rsa/itests/tcp/api/EchoService.java
----------------------------------------------------------------------
diff --git a/itests/testbundle-service-tcp/src/main/java/org/apache/aries/rsa/itests/tcp/api/EchoService.java b/itests/testbundle-service-tcp/src/main/java/org/apache/aries/rsa/itests/tcp/api/EchoService.java
new file mode 100644
index 0000000..877917b
--- /dev/null
+++ b/itests/testbundle-service-tcp/src/main/java/org/apache/aries/rsa/itests/tcp/api/EchoService.java
@@ -0,0 +1,5 @@
+package org.apache.aries.rsa.itests.tcp.api;
+
+public interface EchoService {
+    public String echo(String msg);
+}

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/96a747fb/itests/testbundle-service-tcp/src/main/java/org/apache/aries/rsa/itests/tcp/service/Activator.java
----------------------------------------------------------------------
diff --git a/itests/testbundle-service-tcp/src/main/java/org/apache/aries/rsa/itests/tcp/service/Activator.java b/itests/testbundle-service-tcp/src/main/java/org/apache/aries/rsa/itests/tcp/service/Activator.java
new file mode 100644
index 0000000..184dd2b
--- /dev/null
+++ b/itests/testbundle-service-tcp/src/main/java/org/apache/aries/rsa/itests/tcp/service/Activator.java
@@ -0,0 +1,25 @@
+package org.apache.aries.rsa.itests.tcp.service;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.aries.rsa.itests.tcp.api.EchoService;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.remoteserviceadmin.RemoteConstants;
+
+public class Activator implements BundleActivator {
+
+    @Override
+    public void start(BundleContext context) throws Exception {
+        EchoService echoService = new EchoServiceImpl();
+        Dictionary<String, String> props = new Hashtable<String, String>();
+        props.put(RemoteConstants.SERVICE_EXPORTED_INTERFACES, "*");
+        context.registerService(EchoService.class, echoService, props);
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/96a747fb/itests/testbundle-service-tcp/src/main/java/org/apache/aries/rsa/itests/tcp/service/EchoServiceImpl.java
----------------------------------------------------------------------
diff --git a/itests/testbundle-service-tcp/src/main/java/org/apache/aries/rsa/itests/tcp/service/EchoServiceImpl.java b/itests/testbundle-service-tcp/src/main/java/org/apache/aries/rsa/itests/tcp/service/EchoServiceImpl.java
new file mode 100644
index 0000000..a79abb2
--- /dev/null
+++ b/itests/testbundle-service-tcp/src/main/java/org/apache/aries/rsa/itests/tcp/service/EchoServiceImpl.java
@@ -0,0 +1,12 @@
+package org.apache.aries.rsa.itests.tcp.service;
+
+import org.apache.aries.rsa.itests.tcp.api.EchoService;
+
+public class EchoServiceImpl implements EchoService {
+
+    @Override
+    public String echo(String msg) {
+        return msg;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/96a747fb/parent/pom.xml
----------------------------------------------------------------------
diff --git a/parent/pom.xml b/parent/pom.xml
index ffcbf79..0a9683b 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -30,7 +30,7 @@
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <baseline>1.8.0</baseline>
         <baseline.skip>true</baseline.skip>
-        <zookeeper.version>3.4.8</zookeeper.version>
+        <zookeeper.version>3.4.7</zookeeper.version>
         <rsa.version>1.0.0</rsa.version>
         <slf4j.version>1.7.14</slf4j.version>
         <log4j.version>1.2.6</log4j.version>
@@ -58,7 +58,7 @@
 
         <dependency>
             <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-jcl</artifactId>
+            <artifactId>slf4j-jdk14</artifactId>
             <scope>test</scope>
         </dependency>
         <dependency>
@@ -97,7 +97,7 @@
             </dependency>
             <dependency>
                 <groupId>org.slf4j</groupId>
-                <artifactId>slf4j-jcl</artifactId>
+                <artifactId>slf4j-jdk14</artifactId>
                 <version>${slf4j.version}</version>
             </dependency>
             <dependency>
@@ -124,6 +124,41 @@
                 <version>3.2</version>
                 <scope>test</scope>
             </dependency>
+            <dependency>
+            <groupId>org.apache.zookeeper</groupId>
+            <artifactId>zookeeper</artifactId>
+            <version>${zookeeper.version}</version>
+            <exclusions>
+                <exclusion>
+                   <groupId>com.sun.jdmk</groupId>
+                   <artifactId>jmxtools</artifactId>
+                </exclusion>
+                <exclusion>
+                   <groupId>com.sun.jmx</groupId>
+                   <artifactId>jmxri</artifactId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>slf4j-log4j12</artifactId>
+                    <groupId>org.slf4j</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>jline</artifactId>
+                    <groupId>jline</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>netty</artifactId>
+                    <groupId>io.netty</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>log4j</artifactId>
+                    <groupId>log4j</groupId>
+                </exclusion>
+                <exclusion>
+                    <groupId>io.netty</groupId>
+                    <artifactId>netty</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
         </dependencies>
     </dependencyManagement>
 

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/96a747fb/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 3078411..c104d12 100644
--- a/pom.xml
+++ b/pom.xml
@@ -78,6 +78,7 @@
         <module>provider</module>
         <module>discovery</module>
         <module>features</module>
+        <module>itests</module>
     </modules>
 
     <profiles>