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/11 20:43:26 UTC

[27/50] [abbrv] aries-rsa git commit: Switch project setup to Aries

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/provider/tcp/pom.xml
----------------------------------------------------------------------
diff --git a/provider/tcp/pom.xml b/provider/tcp/pom.xml
new file mode 100644
index 0000000..2edfb3b
--- /dev/null
+++ b/provider/tcp/pom.xml
@@ -0,0 +1,28 @@
+<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/xsd/maven-4.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>
+
+    <groupId>org.apache.aries.rsa.provider</groupId>
+    <artifactId>tcp</artifactId>
+    <packaging>bundle</packaging>
+    <name>Aries Remote Service Admin provider TCP</name>
+    <description>Provider for Java Serialization over TCP</description>
+
+    <properties>
+        <topDirectoryLocation>../..</topDirectoryLocation>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.cxf.dosgi</groupId>
+            <artifactId>cxf-dosgi-ri-provider-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+
+</project>

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/Activator.java
----------------------------------------------------------------------
diff --git a/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/Activator.java b/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/Activator.java
new file mode 100644
index 0000000..128c3d8
--- /dev/null
+++ b/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/Activator.java
@@ -0,0 +1,45 @@
+/**
+ * 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.provider.tcp;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.cxf.dosgi.dsw.api.DistributionProvider;
+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 {
+        DistributionProvider provider = new TCPProvider();
+        Dictionary<String, Object> props = new Hashtable<>();
+        props.put(RemoteConstants.REMOTE_INTENTS_SUPPORTED, new String[]{});
+        props.put(RemoteConstants.REMOTE_CONFIGS_SUPPORTED, provider.getSupportedTypes());
+        context.registerService(DistributionProvider.class, provider, props);
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        // unregister happens automatically
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/LoaderObjectInputStream.java
----------------------------------------------------------------------
diff --git a/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/LoaderObjectInputStream.java b/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/LoaderObjectInputStream.java
new file mode 100644
index 0000000..ac60950
--- /dev/null
+++ b/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/LoaderObjectInputStream.java
@@ -0,0 +1,43 @@
+/**
+ * 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.provider.tcp;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamClass;
+
+public class LoaderObjectInputStream extends ObjectInputStream {
+
+    private ClassLoader loader;
+
+    public LoaderObjectInputStream(InputStream in, ClassLoader loader) throws IOException {
+        super(in);
+        this.loader = loader;
+    }
+
+    @Override
+    protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
+        try {
+            return loader.loadClass(desc.getName());
+        } catch (ClassNotFoundException e) {
+            return super.resolveClass(desc);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/LocalHostUtil.java
----------------------------------------------------------------------
diff --git a/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/LocalHostUtil.java b/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/LocalHostUtil.java
new file mode 100644
index 0000000..3f40bd8
--- /dev/null
+++ b/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/LocalHostUtil.java
@@ -0,0 +1,92 @@
+/**
+ * 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.provider.tcp;
+
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+
+/**
+ * Utility methods to get the local address even on a linux host.
+ */
+public final class LocalHostUtil {
+
+    private LocalHostUtil() {
+        // Util Class
+    }
+
+    /**
+     * Returns an InetAddress representing the address of the localhost. Every
+     * attempt is made to find an address for this host that is not the loopback
+     * address. If no other address can be found, the loopback will be returned.
+     *
+     * @return InetAddress the address of localhost
+     * @throws UnknownHostException if there is a problem determining the address
+     */
+    public static InetAddress getLocalHost() throws UnknownHostException {
+        InetAddress localHost = InetAddress.getLocalHost();
+        if (!localHost.isLoopbackAddress()) {
+            return localHost;
+        }
+        InetAddress[] addrs = getAllLocalUsingNetworkInterface();
+        for (InetAddress addr : addrs) {
+            if (!addr.isLoopbackAddress() && !addr.getHostAddress().contains(":")) {
+                return addr;
+            }
+        }
+        return localHost;
+    }
+
+    /**
+     * Utility method that delegates to the methods of NetworkInterface to
+     * determine addresses for this machine.
+     *
+     * @return all addresses found from the NetworkInterfaces
+     * @throws UnknownHostException if there is a problem determining addresses
+     */
+    private static InetAddress[] getAllLocalUsingNetworkInterface() throws UnknownHostException {
+        try {
+            List<InetAddress> addresses = new ArrayList<InetAddress>();
+            Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces();
+            while (e.hasMoreElements()) {
+                NetworkInterface ni = e.nextElement();
+                for (Enumeration<InetAddress> e2 = ni.getInetAddresses(); e2.hasMoreElements();) {
+                    addresses.add(e2.nextElement());
+                }
+            }
+            return addresses.toArray(new InetAddress[] {});
+        } catch (SocketException ex) {
+            throw new UnknownHostException("127.0.0.1");
+        }
+    }
+
+    public static String getLocalIp() {
+        String localIP;
+        try {
+            localIP = getLocalHost().getHostAddress();
+        } catch (Exception e) {
+            localIP = "localhost";
+        }
+        return localIP;
+    }
+}

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/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
new file mode 100644
index 0000000..07c5a05
--- /dev/null
+++ b/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/TCPProvider.java
@@ -0,0 +1,67 @@
+/**
+ * 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.provider.tcp;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Proxy;
+import java.net.URI;
+import java.util.Map;
+
+import org.apache.cxf.dosgi.dsw.api.DistributionProvider;
+import org.apache.cxf.dosgi.dsw.api.Endpoint;
+import org.apache.cxf.dosgi.dsw.api.IntentUnsatisfiedException;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.RemoteConstants;
+
+@SuppressWarnings("rawtypes")
+public class TCPProvider implements DistributionProvider {
+
+    private static final String TCP_CONFIG_TYPE = "aries.tcp";
+
+    @Override
+    public String[] getSupportedTypes() {
+        return new String[] {TCP_CONFIG_TYPE};
+    }
+
+    @Override
+    public Endpoint exportService(Object serviceO, 
+                                  BundleContext serviceContext,
+                                  Map<String, Object> effectiveProperties,
+                                  Class[] exportedInterfaces) {
+        effectiveProperties.put(RemoteConstants.SERVICE_IMPORTED_CONFIGS, getSupportedTypes());
+        return new TcpEndpoint(serviceO, effectiveProperties);
+    }
+
+    @Override
+    public Object importEndpoint(ClassLoader cl, 
+                                 BundleContext consumerContext, 
+                                 Class[] interfaces,
+                                 EndpointDescription endpoint)
+        throws IntentUnsatisfiedException {
+        try {
+            URI address = new URI(endpoint.getId());
+            InvocationHandler handler = new TcpInvocationHandler(cl, address.getHost(), address.getPort());
+            return Proxy.newProxyInstance(cl, interfaces, handler);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/TCPServer.java
----------------------------------------------------------------------
diff --git a/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/TCPServer.java b/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/TCPServer.java
new file mode 100644
index 0000000..e70731d
--- /dev/null
+++ b/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/TCPServer.java
@@ -0,0 +1,118 @@
+/**
+ * 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.provider.tcp;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TCPServer implements Closeable, Runnable {
+    private Logger log = LoggerFactory.getLogger(TCPServer.class);
+    private ServerSocket serverSocket;
+    private Object service;
+    private boolean running;
+    private ExecutorService executor;
+
+    public TCPServer(Object service, String localip, Integer port, int numThreads) {
+        this.service = service;
+        try {
+            this.serverSocket = new ServerSocket(port);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        this.running = true;
+        this.executor = Executors.newCachedThreadPool();
+        for (int c = 0; c < numThreads; c++) {
+            this.executor.execute(this);
+        }
+    }
+    
+    int getPort() {
+        return this.serverSocket.getLocalPort();
+    }
+
+    public void run() {
+        ClassLoader serviceCL = service.getClass().getClassLoader();
+        while (running) {
+            try (
+                Socket socket = this.serverSocket.accept();
+                ObjectInputStream ois = new LoaderObjectInputStream(socket.getInputStream(), serviceCL);
+                ObjectOutputStream objectOutput = new ObjectOutputStream(socket.getOutputStream())
+                ) {
+                String methodName = (String)ois.readObject();
+                Object[] args = (Object[])ois.readObject();
+                Object result = invoke(methodName, args);
+                objectOutput.writeObject(result);
+            } catch (SocketException e) {
+                running = false;
+            } catch (Exception e) {
+                log.warn("Error processing service call.", e);
+            }
+        }
+    }
+
+    private Object invoke(String methodName, Object[] args)
+        throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+        Class<?>[] parameterTypesAr = getTypes(args);
+        Method method = service.getClass().getMethod(methodName, parameterTypesAr);
+        try {
+            return method.invoke(service, args);
+        } catch (Throwable e) {
+            return e;
+        }
+    }
+
+    private Class<?>[] getTypes(Object[] args) {
+        List<Class<?>> parameterTypes = new ArrayList<>();
+        if (args != null) {
+            for (Object arg : args) {
+                parameterTypes.add(arg.getClass());
+            }
+        }
+        Class<?>[] parameterTypesAr = parameterTypes.toArray(new Class[]{});
+        return parameterTypesAr;
+    }
+
+    @Override
+    public void close() throws IOException {
+        this.serverSocket.close();
+        this.running = false;
+        this.executor.shutdown();
+        try {
+            this.executor.awaitTermination(10, TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+        }
+        this.executor.shutdownNow();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/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
new file mode 100644
index 0000000..5bc9d7a
--- /dev/null
+++ b/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/TcpEndpoint.java
@@ -0,0 +1,58 @@
+/**
+ * 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.provider.tcp;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.cxf.dosgi.dsw.api.Endpoint;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.RemoteConstants;
+
+public class TcpEndpoint implements Endpoint {
+    private EndpointDescription epd;
+    private TCPServer tcpServer;
+    
+    public TcpEndpoint(Object service, Map<String, Object> effectiveProperties) {
+        Integer port = getInt(effectiveProperties, "port", 0);
+        String localip = LocalHostUtil.getLocalIp();
+        int numThreads = getInt(effectiveProperties, "numThreads", 10);
+        tcpServer = new TCPServer(service, localip, port, numThreads);
+        effectiveProperties.put(RemoteConstants.ENDPOINT_ID, "tcp://" + localip + ":" + tcpServer.getPort());
+        effectiveProperties.put(RemoteConstants.SERVICE_EXPORTED_CONFIGS, "");
+        this.epd = new EndpointDescription(effectiveProperties);
+    }
+    
+
+    private Integer getInt(Map<String, Object> effectiveProperties, String key, int defaultValue) {
+        String value = (String)effectiveProperties.get(key);
+        return value != null ? Integer.parseInt(value) : defaultValue;
+    }
+
+    @Override
+    public EndpointDescription description() {
+        return this.epd;
+    }
+
+
+    @Override
+    public void close() throws IOException {
+        tcpServer.close();
+    }
+}

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/TcpInvocationHandler.java
----------------------------------------------------------------------
diff --git a/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/TcpInvocationHandler.java b/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/TcpInvocationHandler.java
new file mode 100644
index 0000000..cdbf8ec
--- /dev/null
+++ b/provider/tcp/src/main/java/org/apache/aries/rsa/provider/tcp/TcpInvocationHandler.java
@@ -0,0 +1,68 @@
+/**
+ * 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.provider.tcp;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+public class TcpInvocationHandler implements InvocationHandler {
+    private String host;
+    private int port;
+    private ClassLoader cl;
+
+    public TcpInvocationHandler(ClassLoader cl, String host, int port)
+        throws UnknownHostException, IOException {
+        this.cl = cl;
+        this.host = host;
+        this.port = port;
+
+    }
+
+    @Override
+    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+        try (
+            Socket socket = new Socket(this.host, this.port);
+            ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream())
+            ) {
+            out.writeObject(method.getName());
+            out.writeObject(args);
+            out.flush();
+            return parseResult(socket);
+        } catch (Exception  e) {
+            throw new RuntimeException("Error calling " + host + ":" + port + " method: " + method.getName(), e);
+        }
+    }
+
+    private Object parseResult(Socket socket) throws IOException, ClassNotFoundException, Throwable {
+        try (ObjectInputStream in = new LoaderObjectInputStream(socket.getInputStream(), cl)) {
+            Object result = in.readObject();
+            if (result instanceof Throwable) {
+                throw (Throwable)result;
+            } else {
+                return result;
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/provider/tcp/src/test/java/org/apache/aries/rsa/provider/tcp/TcpProviderTest.java
----------------------------------------------------------------------
diff --git a/provider/tcp/src/test/java/org/apache/aries/rsa/provider/tcp/TcpProviderTest.java b/provider/tcp/src/test/java/org/apache/aries/rsa/provider/tcp/TcpProviderTest.java
new file mode 100644
index 0000000..583c5e1
--- /dev/null
+++ b/provider/tcp/src/test/java/org/apache/aries/rsa/provider/tcp/TcpProviderTest.java
@@ -0,0 +1,107 @@
+/**
+ * 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.provider.tcp;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.aries.rsa.provider.tcp.myservice.MyService;
+import org.apache.aries.rsa.provider.tcp.myservice.MyServiceImpl;
+import org.apache.cxf.dosgi.dsw.api.Endpoint;
+import org.apache.cxf.dosgi.dsw.api.EndpointHelper;
+import org.easymock.EasyMock;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+
+public class TcpProviderTest {
+
+    private static final int NUM_CALLS = 100;
+    private MyService myServiceProxy;
+    private Endpoint ep;
+    
+    @Before
+    public void createServerAndProxy() {
+        Class<?>[] exportedInterfaces = new Class[] {MyService.class};
+        TCPProvider provider = new TCPProvider();
+        Map<String, Object> props = new HashMap<String, Object>();
+        EndpointHelper.addObjectClass(props, exportedInterfaces);
+        MyService myService = new MyServiceImpl();
+        BundleContext bc = EasyMock.mock(BundleContext.class);
+        ep = provider.exportService(myService, bc, props, exportedInterfaces);
+        myServiceProxy = (MyService)provider.importEndpoint(MyService.class.getClassLoader(), 
+                                                            bc,
+                                                            exportedInterfaces, 
+                                                            ep.description());
+    }
+
+    @Test
+    public void testPerf() throws IOException, InterruptedException {
+        runPerfTest(myServiceProxy);
+        String msg = "test";
+        String result = myServiceProxy.echo(msg);
+        Assert.assertEquals(msg, result);
+    }
+    
+    @Test
+    public void testCall() throws IOException, InterruptedException {
+        myServiceProxy.call("test");
+    }
+    
+    @Test
+    public void testCallOneway() throws IOException, InterruptedException {
+        myServiceProxy.callOneWay("test");
+    }
+    
+    @After
+    public void close() throws IOException {
+        ep.close();
+    }
+
+    private void runPerfTest(final MyService myServiceProxy2) throws InterruptedException {
+        StringBuilder msg = new StringBuilder();
+        for (int c = 0; c < 1000; c++) {
+            msg.append("testing123");
+        }
+        final String msg2 = msg.toString();
+        ExecutorService executor = Executors.newFixedThreadPool(10);
+        Runnable task = new Runnable() {
+            
+            @Override
+            public void run() {
+                String result = myServiceProxy2.echo(msg2);
+                Assert.assertEquals(msg2, result);
+            }
+        };
+        long start = System.currentTimeMillis();
+        for (int c = 0; c < NUM_CALLS; c++) {
+            executor.execute(task);
+        }
+        executor.shutdown();
+        executor.awaitTermination(100, TimeUnit.SECONDS);
+        long tps = NUM_CALLS * 1000 / (System.currentTimeMillis() - start);
+        System.out.println(tps + " tps");
+    }
+}

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/provider/tcp/src/test/java/org/apache/aries/rsa/provider/tcp/myservice/MyService.java
----------------------------------------------------------------------
diff --git a/provider/tcp/src/test/java/org/apache/aries/rsa/provider/tcp/myservice/MyService.java b/provider/tcp/src/test/java/org/apache/aries/rsa/provider/tcp/myservice/MyService.java
new file mode 100644
index 0000000..e9d56bf
--- /dev/null
+++ b/provider/tcp/src/test/java/org/apache/aries/rsa/provider/tcp/myservice/MyService.java
@@ -0,0 +1,30 @@
+/**
+ * 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.provider.tcp.myservice;
+
+import javax.jws.Oneway;
+
+public interface MyService {
+    String echo(String msg);
+    void call(String msg);
+
+    // Oneway not yet supported
+    @Oneway
+    void callOneWay(String msg);
+}

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/provider/tcp/src/test/java/org/apache/aries/rsa/provider/tcp/myservice/MyServiceImpl.java
----------------------------------------------------------------------
diff --git a/provider/tcp/src/test/java/org/apache/aries/rsa/provider/tcp/myservice/MyServiceImpl.java b/provider/tcp/src/test/java/org/apache/aries/rsa/provider/tcp/myservice/MyServiceImpl.java
new file mode 100644
index 0000000..5f469ed
--- /dev/null
+++ b/provider/tcp/src/test/java/org/apache/aries/rsa/provider/tcp/myservice/MyServiceImpl.java
@@ -0,0 +1,36 @@
+/**
+ * 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.provider.tcp.myservice;
+
+public class MyServiceImpl implements MyService {
+
+    @Override
+    public String echo(String msg) {
+        return msg;
+    }
+
+    @Override
+    public void call(String msg) {
+    }
+
+    @Override
+    public void callOneWay(String msg) {
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/repository/pom.xml
----------------------------------------------------------------------
diff --git a/repository/pom.xml b/repository/pom.xml
new file mode 100644
index 0000000..06c85aa
--- /dev/null
+++ b/repository/pom.xml
@@ -0,0 +1,83 @@
+<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/xsd/maven-4.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>repository</artifactId>
+    <packaging>pom</packaging>
+    <properties>
+        <local.index.policy>ALLOWED</local.index.policy>
+    </properties>
+    <profiles>
+        <profile>
+            <id>RunningInCI</id>
+            <activation>
+                <property>
+                    <name>running.in.ci</name>
+                    <value>true</value>
+                </property>
+            </activation>
+            <properties>
+                <local.url.policy>FORBIDDEN</local.url.policy>
+            </properties>
+        </profile>
+        <profile>
+            <id>apache-release</id>
+            <properties>
+                <local.url.policy>FORBIDDEN</local.url.policy>
+            </properties>
+        </profile>
+    </profiles>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>biz.aQute.bnd</groupId>
+                <artifactId>bnd-indexer-maven-plugin</artifactId>
+                <version>${bnd.version}</version>
+                <configuration>
+                    <localURLs>${local.index.policy}</localURLs>
+                    <includeTransitive>true</includeTransitive>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>index</id>
+                        <goals>
+                            <goal>index</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.aries.rsa</groupId>
+            <artifactId>rsa</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.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.discovery</groupId>
+            <artifactId>zookeeper-server-config</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/rsa-api/bnd.bnd
----------------------------------------------------------------------
diff --git a/rsa-api/bnd.bnd b/rsa-api/bnd.bnd
new file mode 100644
index 0000000..d756067
--- /dev/null
+++ b/rsa-api/bnd.bnd
@@ -0,0 +1,6 @@
+Import-Package:\
+	org.osgi.service.remoteserviceadmin,\
+	org.osgi.service.event
+Export-Package:\
+	org.osgi.service.remoteserviceadmin,\
+	org.osgi.service.event

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/rsa-api/pom.xml
----------------------------------------------------------------------
diff --git a/rsa-api/pom.xml b/rsa-api/pom.xml
new file mode 100644
index 0000000..e0dd296
--- /dev/null
+++ b/rsa-api/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>parent</artifactId>
+        <version>1.8-SNAPSHOT</version>
+        <relativePath>../parent/pom.xml</relativePath>
+    </parent>
+    
+    <artifactId>rsa-api</artifactId>
+    <packaging>bundle</packaging>
+    <name>Aries Remote Service Admin Compendium APIs</name>
+    
+    <description>
+        The minimal set of OSGi Compendium APIs required by DOSGi.
+        This bundle should be deployed instead of the full OSGi Compendium bundle to prevent issues
+        caused by multiple exports of other OSGi APIs that are not used by DOSGi. See DOSGI-208.
+    </description>
+
+</project>

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/rsa/bnd.bnd
----------------------------------------------------------------------
diff --git a/rsa/bnd.bnd b/rsa/bnd.bnd
new file mode 100644
index 0000000..d32545d
--- /dev/null
+++ b/rsa/bnd.bnd
@@ -0,0 +1,5 @@
+Bundle-Activator: org.apache.cxf.dosgi.dsw.service.Activator
+Export-Package: \
+	org.apache.cxf.dosgi.dsw.api,\
+	org.apache.cxf.dosgi.dsw.service,\
+	org.osgi.service.remoteserviceadmin

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/rsa/pom.xml
----------------------------------------------------------------------
diff --git a/rsa/pom.xml b/rsa/pom.xml
new file mode 100644
index 0000000..b220283
--- /dev/null
+++ b/rsa/pom.xml
@@ -0,0 +1,27 @@
+<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/xsd/maven-4.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>core</artifactId>
+    <packaging>bundle</packaging>
+    <name>Aries Remote Service Admin Core</name>
+    <description>The Remote Service Admin as described in the OSGi Remote Service Admin specification</description>
+
+    <properties>
+        <topDirectoryLocation>..</topDirectoryLocation>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.aries.rsa</groupId>
+            <artifactId>spi</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/Activator.java
----------------------------------------------------------------------
diff --git a/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/Activator.java b/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/Activator.java
new file mode 100644
index 0000000..4c4d7ad
--- /dev/null
+++ b/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/Activator.java
@@ -0,0 +1,37 @@
+/**
+ * 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.cxf.dosgi.dsw.service;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+public class Activator implements BundleActivator {
+
+    private DistributionProviderTracker tracker;
+
+    public void start(BundleContext bundlecontext) throws Exception {
+        tracker = new DistributionProviderTracker(bundlecontext);
+        tracker.open();
+    }
+
+    public void stop(BundleContext context) throws Exception {
+        tracker.close();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/ClientServiceFactory.java
----------------------------------------------------------------------
diff --git a/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/ClientServiceFactory.java b/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/ClientServiceFactory.java
new file mode 100644
index 0000000..7c292db
--- /dev/null
+++ b/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/ClientServiceFactory.java
@@ -0,0 +1,110 @@
+/**
+ * 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.cxf.dosgi.dsw.service;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.cxf.dosgi.dsw.api.DistributionProvider;
+import org.apache.cxf.dosgi.dsw.api.IntentUnsatisfiedException;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.wiring.BundleWiring;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@SuppressWarnings("rawtypes")
+public class ClientServiceFactory implements ServiceFactory {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ClientServiceFactory.class);
+
+    private EndpointDescription endpoint;
+    private DistributionProvider handler;
+    private ImportRegistrationImpl importRegistration;
+
+    private boolean closeable;
+    private int serviceCounter;
+
+    public ClientServiceFactory(EndpointDescription endpoint,
+                                DistributionProvider handler, ImportRegistrationImpl ir) {
+        this.endpoint = endpoint;
+        this.handler = handler;
+        this.importRegistration = ir;
+    }
+
+    public Object getService(final Bundle requestingBundle, final ServiceRegistration sreg) {
+        List<String> interfaceNames = endpoint.getInterfaces();
+        final BundleContext consumerContext = requestingBundle.getBundleContext();
+        final ClassLoader consumerLoader = requestingBundle.adapt(BundleWiring.class).getClassLoader();
+        try {
+            LOG.debug("getService() from serviceFactory for {}", interfaceNames);
+            final List<Class<?>> interfaces = new ArrayList<Class<?>>();
+            for (String ifaceName : interfaceNames) {
+                interfaces.add(consumerLoader.loadClass(ifaceName));
+            }
+            Object proxy = AccessController.doPrivileged(new PrivilegedAction<Object>() {
+                public Object run() {
+                    Class<?>[] ifAr = interfaces.toArray(new Class[]{});
+                    return handler.importEndpoint(consumerLoader, consumerContext, ifAr, endpoint);
+                }
+            });
+
+            synchronized (this) {
+                serviceCounter++;
+            }
+            return proxy;
+        } catch (IntentUnsatisfiedException iue) {
+            LOG.info("Did not create proxy for {} because intent {} could not be satisfied",
+                    interfaceNames, iue.getIntent());
+        } catch (Exception e) {
+            LOG.warn("Problem creating a remote proxy for {}", interfaceNames, e);
+        }
+        return null;
+    }
+
+    public void ungetService(Bundle requestingBundle, ServiceRegistration sreg, Object serviceObject) {
+        String[] interfaces = (String[])sreg.getReference().getProperty(org.osgi.framework.Constants.OBJECTCLASS);
+        LOG.info("Releasing a client object, interfaces: {}", Arrays.toString(interfaces));
+
+        synchronized (this) {
+            serviceCounter--;
+            LOG.debug("Services still provided by this ServiceFactory: {}", serviceCounter);
+            closeIfUnused();
+        }
+    }
+
+    public void setCloseable(boolean closeable) {
+        synchronized (this) {
+            this.closeable = closeable;
+            closeIfUnused();
+        }
+    }
+
+    private synchronized void closeIfUnused() {
+        if (serviceCounter <= 0 && closeable) {
+            importRegistration.closeAll();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/DistributionProviderTracker.java
----------------------------------------------------------------------
diff --git a/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/DistributionProviderTracker.java b/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/DistributionProviderTracker.java
new file mode 100644
index 0000000..675fcc6
--- /dev/null
+++ b/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/DistributionProviderTracker.java
@@ -0,0 +1,73 @@
+/**
+ * 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.cxf.dosgi.dsw.service;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.cxf.dosgi.dsw.api.DistributionProvider;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.remoteserviceadmin.RemoteServiceAdmin;
+import org.osgi.util.tracker.ServiceTracker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@SuppressWarnings("rawtypes")
+public class DistributionProviderTracker extends ServiceTracker<DistributionProvider, ServiceRegistration> {
+    private static final Logger LOG = LoggerFactory.getLogger(Activator.class);
+
+    public DistributionProviderTracker(BundleContext context) {
+        super(context, DistributionProvider.class, null);
+    }
+
+    @Override
+    public ServiceRegistration addingService(ServiceReference<DistributionProvider> reference) {
+        LOG.debug("RemoteServiceAdmin Implementation is starting up");
+        DistributionProvider provider = context.getService(reference);
+        BundleContext apiContext = getAPIContext();
+        RemoteServiceAdminCore rsaCore = new RemoteServiceAdminCore(context, 
+                                                                    apiContext, 
+                                                                    provider);
+        RemoteServiceadminFactory rsaf = new RemoteServiceadminFactory(rsaCore);
+        Dictionary<String, Object> props = new Hashtable<String, Object>();
+        props.put("remote.intents.supported", reference.getProperty("remote.intents.supported"));
+        props.put("remote.configs.supported", reference.getProperty("remote.configs.supported"));
+        LOG.info("Registering RemoteServiceAdmin for provider " + provider.getClass().getName());
+        return context.registerService(RemoteServiceAdmin.class.getName(), rsaf, props);
+    }
+
+    protected BundleContext getAPIContext() {
+        Bundle apiBundle = FrameworkUtil.getBundle(DistributionProvider.class);
+        BundleContext apiContext = apiBundle.getBundleContext();
+        return apiContext;
+    }
+    
+    @Override
+    public void removedService(ServiceReference<DistributionProvider> reference,
+                               ServiceRegistration reg) {
+        LOG.debug("RemoteServiceAdmin Implementation is shutting down now");
+        reg.unregister();
+        super.removedService(reference, reg);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/EventAdminHelper.java
----------------------------------------------------------------------
diff --git a/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/EventAdminHelper.java b/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/EventAdminHelper.java
new file mode 100644
index 0000000..4868efa
--- /dev/null
+++ b/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/EventAdminHelper.java
@@ -0,0 +1,151 @@
+/**
+ * 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.cxf.dosgi.dsw.service;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.Version;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.RemoteServiceAdminEvent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class EventAdminHelper {
+
+    private static final Logger LOG = LoggerFactory.getLogger(EventAdminHelper.class);
+
+    private BundleContext bctx;
+
+    public EventAdminHelper(BundleContext bc) {
+        bctx = bc;
+    }
+
+    private Event createEvent(Map<String, Object> props, String type) {
+        String topic = "org/osgi/service/remoteserviceadmin/" + type;
+        props.put("bundle", bctx.getBundle());
+        props.put("bundle.id", bctx.getBundle().getBundleId());
+        props.put("bundle.symbolicname", bctx.getBundle().getSymbolicName());
+
+        String version = (String)bctx.getBundle().getHeaders().get("Bundle-Version");
+        Version v = version != null ? new Version(version) : Version.emptyVersion;
+        setIfNotNull(props, "bundle.version", v);
+
+        return new Event(topic, props);
+    }
+
+    public void notifyEventAdmin(RemoteServiceAdminEvent rsae) {
+        String topic = remoteServiceAdminEventTypeToString(rsae.getType());
+
+        Map<String, Object> props = new HashMap<String, Object>();
+        setIfNotNull(props, "cause", rsae.getException());
+
+        EndpointDescription endpoint = null;
+        if (rsae.getImportReference() != null) {
+            endpoint = ((ImportRegistrationImpl)rsae.getImportReference()).getImportedEndpointAlways();
+            setIfNotNull(props, "import.registration", endpoint);
+        } else if (rsae.getExportReference() != null) {
+            endpoint = rsae.getExportReference().getExportedEndpoint();
+            setIfNotNull(props, "export.registration", endpoint);
+        }
+
+        if (endpoint != null) {
+            setIfNotNull(props, "service.remote.id", endpoint.getServiceId());
+            setIfNotNull(props, "service.remote.uuid", endpoint.getFrameworkUUID());
+            setIfNotNull(props, "service.remote.uri", endpoint.getId());
+            setIfNotNull(props, "objectClass", endpoint.getInterfaces().toArray());
+            setIfNotNull(props, "service.imported.configs", endpoint.getConfigurationTypes());
+        }
+        props.put("timestamp", System.currentTimeMillis());
+        props.put("event", rsae);
+
+        Event event = createEvent(props, topic);
+        notifyEventAdmins(topic, event);
+    }
+
+    @SuppressWarnings({
+     "rawtypes", "unchecked"
+    })
+    private void notifyEventAdmins(String topic, Event event) {
+        ServiceReference[] refs = null;
+        try {
+            refs = bctx.getAllServiceReferences(EventAdmin.class.getName(), null);
+        } catch (InvalidSyntaxException e) {
+            LOG.error("Failed to get EventAdmin: " + e.getMessage(), e);
+        }
+
+        if (refs != null) {
+            LOG.debug("Publishing event to {} EventAdmins; Topic:[{}]", refs.length, topic);
+            for (ServiceReference serviceReference : refs) {
+                EventAdmin eventAdmin = (EventAdmin) bctx.getService(serviceReference);
+                try {
+                    eventAdmin.postEvent(event);
+                } finally {
+                    if (eventAdmin != null) {
+                        bctx.ungetService(serviceReference);
+                    }
+                }
+            }
+        }
+    }
+
+    private <K, V> void setIfNotNull(Map<K, V> map, K key, V val) {
+        if (val != null) {
+            map.put(key, val);
+        }
+    }
+
+    private static String remoteServiceAdminEventTypeToString(int type) {
+        String retval;
+        switch (type) {
+        case RemoteServiceAdminEvent.EXPORT_ERROR:
+            retval = "EXPORT_ERROR";
+            break;
+        case RemoteServiceAdminEvent.EXPORT_REGISTRATION:
+            retval = "EXPORT_REGISTRATION";
+            break;
+        case RemoteServiceAdminEvent.EXPORT_UNREGISTRATION:
+            retval = "EXPORT_UNREGISTRATION";
+            break;
+        case RemoteServiceAdminEvent.EXPORT_WARNING:
+            retval = "EXPORT_WARNING";
+            break;
+        case RemoteServiceAdminEvent.IMPORT_ERROR:
+            retval = "IMPORT_ERROR";
+            break;
+        case RemoteServiceAdminEvent.IMPORT_REGISTRATION:
+            retval = "IMPORT_REGISTRATION";
+            break;
+        case RemoteServiceAdminEvent.IMPORT_UNREGISTRATION:
+            retval = "IMPORT_UNREGISTRATION";
+            break;
+        case RemoteServiceAdminEvent.IMPORT_WARNING:
+            retval = "IMPORT_WARNING";
+            break;
+        default:
+            retval = "UNKNOWN_EVENT";
+        }
+        return retval;
+    }
+}

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/EventProducer.java
----------------------------------------------------------------------
diff --git a/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/EventProducer.java b/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/EventProducer.java
new file mode 100644
index 0000000..26a46ab
--- /dev/null
+++ b/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/EventProducer.java
@@ -0,0 +1,114 @@
+/**
+ * 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.cxf.dosgi.dsw.service;
+
+import java.util.List;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.ExportRegistration;
+import org.osgi.service.remoteserviceadmin.ImportRegistration;
+import org.osgi.service.remoteserviceadmin.RemoteServiceAdminEvent;
+import org.osgi.service.remoteserviceadmin.RemoteServiceAdminListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class EventProducer {
+
+    private static final Logger LOG = LoggerFactory.getLogger(EventProducer.class);
+    private final BundleContext bctx;
+    private final EventAdminHelper eaHelper;
+
+    public EventProducer(BundleContext bc) {
+        bctx = bc;
+        eaHelper = new EventAdminHelper(bctx);
+    }
+
+    protected void publishNotification(List<ExportRegistration> erl) {
+        for (ExportRegistration exportRegistration : erl) {
+            publishNotification(exportRegistration);
+        }
+    }
+
+    protected void publishNotification(ExportRegistration er) {
+        int type = er.getException() == null
+            ? RemoteServiceAdminEvent.EXPORT_REGISTRATION
+            : RemoteServiceAdminEvent.EXPORT_ERROR;
+        notify(type, null, er);
+    }
+
+    protected void publishNotification(ImportRegistration ir) {
+        int type = ir.getException() == null
+            ? RemoteServiceAdminEvent.IMPORT_REGISTRATION
+            : RemoteServiceAdminEvent.IMPORT_ERROR;
+        notify(type, ir, null);
+    }
+
+    public void notifyRemoval(ExportRegistration er) {
+        notify(RemoteServiceAdminEvent.EXPORT_UNREGISTRATION, null, er);
+    }
+
+    public void notifyRemoval(ImportRegistration ir) {
+        notify(RemoteServiceAdminEvent.IMPORT_UNREGISTRATION, ir, null);
+    }
+
+    // only one of ir or er must be set, and the other must be null
+    private void notify(int type, ImportRegistration ir, ExportRegistration er) {
+        try {
+            RemoteServiceAdminEvent event = ir != null
+                ? new RemoteServiceAdminEvent(type, bctx.getBundle(), ir.getImportReference(), ir.getException())
+                : new RemoteServiceAdminEvent(type, bctx.getBundle(), er.getExportReference(), er.getException());
+            notifyListeners(event);
+            eaHelper.notifyEventAdmin(event);
+        } catch (IllegalStateException ise) {
+            LOG.debug("can't send notifications since bundle context is no longer valid");
+        }
+    }
+
+    @SuppressWarnings({
+     "rawtypes", "unchecked"
+    })
+    private void notifyListeners(RemoteServiceAdminEvent rsae) {
+        try {
+            ServiceReference[] listenerRefs = bctx.getServiceReferences(
+                    RemoteServiceAdminListener.class.getName(), null);
+            if (listenerRefs != null) {
+                for (ServiceReference sref : listenerRefs) {
+                    RemoteServiceAdminListener rsal = (RemoteServiceAdminListener)bctx.getService(sref);
+                    if (rsal != null) {
+                        try {
+                            Bundle bundle = sref.getBundle();
+                            if (bundle != null) {
+                                LOG.debug("notify RemoteServiceAdminListener {} of bundle {}",
+                                        rsal, bundle.getSymbolicName());
+                                rsal.remoteAdminEvent(rsae);
+                            }
+                        } finally {
+                            bctx.ungetService(sref);
+                        }
+                    }
+                }
+            }
+        } catch (InvalidSyntaxException e) {
+            LOG.error(e.getMessage(), e);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/ExportReferenceImpl.java
----------------------------------------------------------------------
diff --git a/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/ExportReferenceImpl.java b/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/ExportReferenceImpl.java
new file mode 100644
index 0000000..497aa9c
--- /dev/null
+++ b/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/ExportReferenceImpl.java
@@ -0,0 +1,77 @@
+/**
+ * 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.cxf.dosgi.dsw.service;
+
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.ExportReference;
+
+@SuppressWarnings("rawtypes")
+public class ExportReferenceImpl implements ExportReference {
+
+    private ServiceReference serviceReference;
+    private EndpointDescription endpoint;
+
+    public ExportReferenceImpl(ServiceReference serviceReference, EndpointDescription endpoint) {
+        this.serviceReference = serviceReference;
+        this.endpoint = endpoint;
+    }
+
+    public ExportReferenceImpl(ExportReference exportReference) {
+        this(exportReference.getExportedService(), exportReference.getExportedEndpoint());
+    }
+
+    public EndpointDescription getExportedEndpoint() {
+        return endpoint;
+    }
+
+    public ServiceReference getExportedService() {
+        return serviceReference;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + (endpoint == null ? 0 : endpoint.hashCode());
+        result = prime * result + (serviceReference == null ? 0 : serviceReference.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        ExportReferenceImpl other = (ExportReferenceImpl) obj;
+        boolean ed = endpoint == null ? other.endpoint == null
+                : endpoint.equals(other.endpoint);
+        boolean sr = serviceReference == null ? other.serviceReference == null
+                : serviceReference.equals(other.serviceReference);
+        return ed && sr;
+    }
+
+    synchronized void close() {
+        this.endpoint = null;
+        this.serviceReference = null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/ExportRegistrationImpl.java
----------------------------------------------------------------------
diff --git a/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/ExportRegistrationImpl.java b/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/ExportRegistrationImpl.java
new file mode 100644
index 0000000..d80bd40
--- /dev/null
+++ b/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/ExportRegistrationImpl.java
@@ -0,0 +1,152 @@
+/**
+ * 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.cxf.dosgi.dsw.service;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.cxf.dosgi.dsw.api.Endpoint;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@SuppressWarnings("rawtypes")
+public class ExportRegistrationImpl implements ExportRegistration {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ExportRegistrationImpl.class);
+
+    private final RemoteServiceAdminCore rsaCore;
+    private final ExportReferenceImpl exportReference;
+    private final Closeable server;
+    private final Throwable exception;
+
+    private final ExportRegistrationImpl parent;
+    private int instanceCount;
+    private volatile boolean closed;
+
+    private ExportRegistrationImpl(ExportRegistrationImpl parent, RemoteServiceAdminCore rsaCore,
+            ExportReferenceImpl exportReference, Closeable server, Throwable exception) {
+        this.parent = parent != null ? parent.parent : this; // a parent points to itself
+        this.parent.addInstance();
+        this.rsaCore = rsaCore;
+        this.exportReference = exportReference;
+        this.server = server;
+        this.exception = exception;
+    }
+
+    // create a clone of the provided ExportRegistrationImpl that is linked to it
+    public ExportRegistrationImpl(ExportRegistrationImpl parent) {
+        this(parent, parent.rsaCore, new ExportReferenceImpl(parent.exportReference),
+            parent.server, parent.exception);
+    }
+
+    // create a new (parent) instance which was exported successfully with the given server
+    public ExportRegistrationImpl(ServiceReference sref, Endpoint endpoint, RemoteServiceAdminCore rsaCore) {
+        this(null, rsaCore, new ExportReferenceImpl(sref, endpoint.description()), endpoint, null);
+    }
+
+    // create a new (parent) instance which failed to be exported with the given exception
+    public ExportRegistrationImpl(RemoteServiceAdminCore rsaCore, Throwable exception) {
+        this(null, rsaCore, null, null, exception);
+    }
+
+    private void ensureParent() {
+        if (parent != this) {
+            throw new IllegalStateException("this method may only be called on the parent");
+        }
+    }
+
+    public ExportReference getExportReference() {
+        if (exportReference == null) {
+            throw new IllegalStateException(getException());
+        }
+        return closed ? null : exportReference;
+    }
+
+    public Throwable getException() {
+        return closed ? null : exception;
+    }
+
+    public final void close() {
+        synchronized (this) {
+            if (closed) {
+                return;
+            }
+            closed = true;
+        }
+
+        rsaCore.removeExportRegistration(this);
+        exportReference.close();
+        parent.removeInstance();
+    }
+
+    private void addInstance() {
+        ensureParent();
+        synchronized (this) {
+            instanceCount++;
+        }
+    }
+
+    private void removeInstance() {
+        ensureParent();
+        synchronized (this) {
+            instanceCount--;
+            if (instanceCount <= 0) {
+                LOG.debug("really closing ExportRegistration now!");
+
+                if (server != null) {
+                    try {
+                        server.close();
+                    } catch (IOException e) {
+                        LOG.warn("Error closing ExportRegistration", e);
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public String toString() {
+        if (closed) {
+            return "ExportRegistration closed";
+        }
+        EndpointDescription endpoint = getExportReference().getExportedEndpoint();
+        ServiceReference serviceReference = getExportReference().getExportedService();
+        String r = "EndpointDescription for ServiceReference " + serviceReference;
+
+        r += "\n*** EndpointDescription: ****\n";
+        if (endpoint == null) {
+            r += "---> NULL <---- \n";
+        } else {
+            Set<Map.Entry<String, Object>> props = endpoint.getProperties().entrySet();
+            for (Map.Entry<String, Object> entry : props) {
+                Object value = entry.getValue();
+                r += entry.getKey() + " => "
+                    + (value instanceof Object[] ? Arrays.toString((Object[]) value) : value) + "\n";
+            }
+        }
+        return r;
+    }
+}

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/ImportRegistrationImpl.java
----------------------------------------------------------------------
diff --git a/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/ImportRegistrationImpl.java b/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/ImportRegistrationImpl.java
new file mode 100644
index 0000000..2b896db
--- /dev/null
+++ b/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/ImportRegistrationImpl.java
@@ -0,0 +1,230 @@
+/**
+ * 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.cxf.dosgi.dsw.service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.ImportReference;
+import org.osgi.service.remoteserviceadmin.ImportRegistration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@SuppressWarnings("rawtypes")
+public class ImportRegistrationImpl implements ImportRegistration, ImportReference {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ImportRegistrationImpl.class);
+
+    private volatile Throwable exception;
+    private volatile ServiceRegistration importedService; // used only in parent
+    private EndpointDescription endpoint;
+    private volatile ClientServiceFactory clientServiceFactory;
+    private RemoteServiceAdminCore rsaCore;
+    private boolean closed;
+    private boolean detached; // used only in parent
+
+    private ImportRegistrationImpl parent;
+    private List<ImportRegistrationImpl> children; // used only in parent
+
+    public ImportRegistrationImpl(Throwable ex) {
+        exception = ex;
+        initParent();
+    }
+
+    public ImportRegistrationImpl(EndpointDescription endpoint, RemoteServiceAdminCore rsac) {
+        this.endpoint = endpoint;
+        this.rsaCore = rsac;
+        initParent();
+    }
+
+    /**
+     * Creates a clone of the given parent instance.
+     */
+    public ImportRegistrationImpl(ImportRegistrationImpl ir) {
+        // we always want a link to the parent...
+        parent = ir.getParent();
+        exception = parent.getException();
+        endpoint = parent.getImportedEndpointDescription();
+        clientServiceFactory = parent.clientServiceFactory;
+        rsaCore = parent.rsaCore;
+
+        parent.instanceAdded(this);
+    }
+
+    private void initParent() {
+        parent = this;
+        children = new ArrayList<ImportRegistrationImpl>(1);
+    }
+
+    private void ensureParent() {
+        if (parent != this) {
+            throw new IllegalStateException("this method may only be called on the parent");
+        }
+    }
+
+    /**
+     * Called on parent when a child is added.
+     *
+     * @param iri the child
+     */
+    private synchronized void instanceAdded(ImportRegistrationImpl iri) {
+        ensureParent();
+        children.add(iri);
+    }
+
+    /**
+     * Called on parent when a child is closed.
+     *
+     * @param iri the child
+     */
+    private void instanceClosed(ImportRegistrationImpl iri) {
+        ensureParent();
+        synchronized (this) {
+            children.remove(iri);
+            if (!children.isEmpty() || detached || !closed) {
+                return;
+            }
+            detached = true;
+        }
+
+        LOG.debug("really closing ImportRegistration now");
+
+        if (importedService != null) {
+            try {
+                importedService.unregister();
+            } catch (IllegalStateException ise) {
+                LOG.debug("imported service is already unregistered");
+            }
+            importedService = null;
+        }
+        if (clientServiceFactory != null) {
+            clientServiceFactory.setCloseable(true);
+        }
+    }
+
+    public void close() {
+        LOG.debug("close() called");
+
+        synchronized (this) {
+            if (isInvalid()) {
+                return;
+            }
+            closed = true;
+        }
+        rsaCore.removeImportRegistration(this);
+        parent.instanceClosed(this);
+    }
+
+    /**
+     * Closes all ImportRegistrations which share the same parent as this one.
+     */
+    public void closeAll() {
+        if (this == parent) {
+            LOG.info("closing down all child ImportRegistrations");
+
+            // we must iterate over a copy of children since close() removes the child
+            // from the list (which would cause a ConcurrentModificationException)
+            for (ImportRegistrationImpl ir : copyChildren()) {
+                ir.close();
+            }
+            this.close();
+        } else {
+            parent.closeAll();
+        }
+    }
+
+    private List<ImportRegistrationImpl> copyChildren() {
+        synchronized (this) {
+            return new ArrayList<ImportRegistrationImpl>(children);
+        }
+    }
+
+    public EndpointDescription getImportedEndpointDescription() {
+        return isInvalid() ? null : endpoint;
+    }
+
+    @Override
+    public EndpointDescription getImportedEndpoint() {
+        return getImportedEndpointDescription();
+    }
+
+    @Override
+    public ServiceReference getImportedService() {
+        return isInvalid() || parent.importedService == null ? null : parent.importedService.getReference();
+    }
+
+    @Override
+    public ImportReference getImportReference() {
+        return this;
+    }
+
+    @Override
+    public Throwable getException() {
+        return exception;
+    }
+
+    public void setException(Throwable ex) {
+        exception = ex;
+    }
+
+    private synchronized boolean isInvalid() {
+        return exception != null || closed;
+    }
+
+    /**
+     * Sets the {@link ServiceRegistration} representing the locally
+     * registered {@link ClientServiceFactory} service which provides
+     * proxies to the remote imported service. It is set only on the parent.
+     *
+     * @param sreg the ServiceRegistration
+     */
+    public void setImportedServiceRegistration(ServiceRegistration sreg) {
+        ensureParent();
+        importedService = sreg;
+    }
+
+    /**
+     * Sets the {@link ClientServiceFactory} which is the implementation
+     * of the locally registered service which provides proxies to the
+     * remote imported service. It is set only on the parent.
+     *
+     * @param csf the ClientServiceFactory
+     */
+    public void setClientServiceFactory(ClientServiceFactory csf) {
+        ensureParent();
+        clientServiceFactory = csf;
+    }
+
+    public ImportRegistrationImpl getParent() {
+        return parent;
+    }
+
+    /**
+     * Returns the imported endpoint even if this
+     * instance is closed or has an exception.
+     *
+     * @return the imported endpoint
+     */
+    public EndpointDescription getImportedEndpointAlways() {
+        return endpoint;
+    }
+}

http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/PackageUtil.java
----------------------------------------------------------------------
diff --git a/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/PackageUtil.java b/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/PackageUtil.java
new file mode 100644
index 0000000..effcef1
--- /dev/null
+++ b/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/PackageUtil.java
@@ -0,0 +1,85 @@
+/**
+ * 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.cxf.dosgi.dsw.service;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.packageadmin.ExportedPackage;
+import org.osgi.service.packageadmin.PackageAdmin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@SuppressWarnings("deprecation")
+public final class PackageUtil {
+
+    public static final Logger LOG = LoggerFactory.getLogger(PackageUtil.class);
+
+    private PackageUtil() {
+    }
+
+    /**
+     * Tries to retrieve the version of iClass via the PackageAdmin.
+     *
+     * @param iClass tThe interface for which the version should be found
+     * @param bc any valid BundleContext
+     * @return the version of the interface or "0.0.0" if no version information could be found or an error
+     *         occurred during the retrieval
+     */
+    public static String getVersion(Class<?> iClass, BundleContext bc) {
+        ServiceReference<PackageAdmin> paRef = bc.getServiceReference(PackageAdmin.class);
+        if (paRef != null) {
+            PackageAdmin pa = bc.getService(paRef);
+            try {
+                Bundle b = pa.getBundle(iClass);
+                if (b == null) {
+                    LOG.info("Unable to find interface version for interface " + iClass.getName()
+                            + ". Falling back to 0.0.0");
+                    return "0.0.0";
+                }
+                LOG.debug("Interface source bundle: {}", b.getSymbolicName());
+
+                ExportedPackage[] ep = pa.getExportedPackages(b);
+                LOG.debug("Exported Packages of the source bundle: {}", (Object)ep);
+
+                String pack = iClass.getPackage().getName();
+                LOG.debug("Looking for Package: {}", pack);
+                if (ep != null) {
+                    for (ExportedPackage p : ep) {
+                        if (p != null
+                            && pack.equals(p.getName())) {
+                            LOG.debug("found package -> Version: {}", p.getVersion());
+                            return p.getVersion().toString();
+                        }
+                    }
+                }
+            } finally {
+                if (pa != null) {
+                    bc.ungetService(paRef);
+                }
+            }
+        } else {
+            LOG.error("Was unable to obtain the package admin service -> can't resolve interface versions");
+        }
+
+        LOG.info("Unable to find interface version for interface " + iClass.getName()
+                 + ". Falling back to 0.0.0");
+        return "0.0.0";
+    }
+}